Android從門外到入門(1)

此文假設所有人都會java的語法.

手把手創(chuàng)建項目

  1. 打開Android studio后, 選擇file-new project-選擇Phone and Tablet-選中Basic Activity, 點擊Next, 輸入名字和選擇api level之后, 點擊Finish.
  2. 此時android studio 自動創(chuàng)建了一個項目并進行sync 操作. 查看對應的AndroidManifest.xml文件, 可以看到已經(jīng)自動創(chuàng)建了一個MainActivity項并配置完成
  3. 然后點擊android studio上的Run按鈕, 此時就會自動打包并安裝到手機上

初識Android文件結構

Android的文件結構, 主要包含幾個重要部分:
1.gradle文件
包括項目的build.gradle文件, 主要負責配置gradle倉庫和打包腳本, 無需過多關注, 以及組件的build.gradle文件, 負責配置安卓版本以及依賴包, 這個文件需要注意, 在導入依賴包時必須使用到.

  • plugin 一般有兩個值可選: 'com.android.application'表示這是一個應用程序模塊, 'com.android.library'表示這是一個庫模塊
apply plugin: 'com.android.application'
  • android 閉包
android {
   compileOptions{  // 這里表示使用java 1.8特性
       sourceCompatibility JavaVersion.VERSION_1_8
       targetCompatibility JavaVersion.VERSION_1_8
   }
   compileSdkVersion 28   //這個表示默認打包為sdk 28版本
   defaultConfig {
       applicationId "com.zzy.xuexiqiangguo"  // 包id
       minSdkVersion 26   // 最低支持的系統(tǒng)版本
       targetSdkVersion 28 // 默認的系統(tǒng)版本
       versionCode 1  //包版本
       versionName "1.0" //包版本
       testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" // 這里表示無界面測試依賴包
   }
   buildTypes {
       release {
           minifyEnabled false
           proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
       }
   }
}
  • dependencies
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')  // 依賴libs/*.jar包
    implementation 'com.android.support:appcompat-v7:28.0.0' // 依賴包
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2' // 測試依賴包
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation project(':rhinojs') // 表示依賴另一個項目
}
  1. AndroidManifest.xml 文件, 該文件配置了app的名字, 活動, 權限等數(shù)據(jù), 必須關注, 在啟動時會從該文件中查找對應的動作.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zzy.xuexiqiangguo">
    <uses-permission android:name="android.permission.INTERNET"/> //這里表示請求網(wǎng)絡權限
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <service  // 這段表示綁定AccessibilityService
            android:name=".RobService"
            android:enabled="true"
            android:exported="true"
            android:label="學習強國"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService" />
        </intent-filter>
        <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/myaccessibility"/>
        </service>
        <activity android:name=".MainActivity"> // 這里表示下面的是啟動Activity
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
  1. main/java目錄, 該目錄下存放了android的代碼.
  2. main/res目錄, 該目錄下存放的是設計/圖片/媒體文件
Android的日志模塊Log

安卓的日志模塊Log, 在這個類中提供了5個不同級別的日志打印方法, 另外提供了logt這個快捷輸入命令用于添加TAG變量

  1. Log.v() 打印最瑣碎的日志信息, 快捷輸入logv
  2. Log.d() 打印調試信息, 快捷輸入logd
  3. Log.i() 打印一些比較重要的信息, 快捷輸入logi
  4. Log.w() 打印一些警告信息, 快捷輸入logw
  5. Log.e() 打印錯誤信息. 快捷輸入loge
    添加了日志打印內容后, 就可以在logcat中看到對應的日志了.

從活動Activity入手

活動在Android中的意思是指包含用戶界面的組件, 主要用于和用戶交互, 我們打開一個app時看到的每一個界面都是一個活動.

創(chuàng)建布局
  1. 在res目錄下新建layout目錄后, 右鍵點擊layout-layout resource file, 此時彈出新建面板, 輸入文件名后點擊ok
  2. 在Design頁中拖動一個button后加入到layout中, 此時可以實時看到button的顯示. 在這里解釋一下各個屬性
<Button
        android:id="@+id/button1" //@+id表示注冊一個新的id到R.id中, 在這里就是注冊R.id.button1
        android:layout_width="match_parent" // 這里表示寬度與父節(jié)點一致
        android:layout_height="wrap_content" // 這里表示高度適應大小
        android:text="Button" /> // text表示button上顯示的文本
活動的基本用法
  1. 在onCreate() 中加載布局: setContentView(R.layout.main_activity);
  2. 使用findViewById方法獲取到對應的View后, 添加事件監(jiān)聽
        Button button = (Button) findViewById(R.id.button1);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "show toast", Toast.LENGTH_SHORT).show();
            }
        });
在AndroidManifest.xml中注冊
  1. 以下中intent-filter表示在啟動時打開MainActivity
        <activity android:name=".MainActivity"  android:label="demo activity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" /> 
            </intent-filter>
        </activity>

完成了以上三個步驟之后, 就算是已經(jīng)完成了一個最簡單的可用的app了.

使用Intent 切換Activity

切換界面, 是程序運行中一個再正常不過的需求

  1. 使用顯式Intent切換
Intent intent = new Intent(MainActivity.this, secondActivity.class);
startActivity(intent);
  1. 使用隱式Intent切換, 一般都是使用這個切換方式
Intent intent = new Intent("android.intent.action.MAIN");
intent.addCategory("android.intent.category.LAUNCHER");
startActivity(intent);
  1. 通過隱式Intent, 還可以用于打開網(wǎng)頁, 打開撥號盤等
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setData(Uri.parse("https://www.baidu.com"));
                startActivity(intent);
                Intent intent = new Intent(Intent.ACTION_DIAL);
                intent.setData(Uri.parse("tel:10086"));
                startActivity(intent);

####### 活動間數(shù)據(jù)傳遞
傳遞數(shù)據(jù)代碼

Intent intent = new Intent("android.intent.action.MAIN");
intent.addCategory("android.intent.category.LAUNCHER");
intent.putExtra("extra_data", "helloworld");
startActivity(intent);

獲取數(shù)據(jù)代碼

        Intent intent = getIntent();
        String data = intent.getExtra("extra_data");

活動的生命周期

  1. 返回棧
    Android中的活動是可以層疊的. 每當啟動一個新的活動, 就會覆蓋在原活動之上, 然后按back鍵會銷毀最上面的活動, 下面的活動重新顯示出來.

  2. 活動狀態(tài)
    每個活動在其生命周期中最多可能會有4種狀態(tài)

    1. 運行狀態(tài)
      當一個活動位于棧頂時, 就處于運行狀態(tài).
    2. 暫停狀態(tài)
      當一個活動不再處于棧頂, 但活動依然可見時, 就處于暫停狀態(tài), 比如說點擊某個按鈕后彈出對話框, 此時對話框處于運行狀態(tài), 對話框下的活動處于暫停狀態(tài). 處于暫停狀態(tài)下的活動仍然是存活的, 系統(tǒng)也不愿意去回收這種活動.
    3. 停止狀態(tài)
      當一個活動不再處于棧頂, 并且不可見時, 就進入停止狀態(tài). 系統(tǒng)會為這種活動保存相應的狀態(tài)和成員變量, 但是當其他地方需要內存時, 停止狀態(tài)的活動可能會被回收.
    4. 銷毀狀態(tài)
      當一個活動從返回棧中移除后就變成銷毀狀態(tài).
  3. 活動的生存期
    Activity類提供了7個回調方法, 覆蓋了生命周期中的每一個環(huán)節(jié), 依次為

    • onCreate() 創(chuàng)建時調用, 這個創(chuàng)建可能是第一次創(chuàng)建, 也有可能是被系統(tǒng)回收后, 從返回棧中取出狀態(tài)重新創(chuàng)建
    • onStart() 活動由不可見狀態(tài)變?yōu)榭梢姞顟B(tài)時調用
    • onResume() 活動準備好與用戶進行交互時調用, 此時活動一定位于棧頂(比如關閉對話框時調用)
    • onPause() 系統(tǒng)準備去啟動或者恢復另一個活動的時候調用
    • onStop() 活動由可見變?yōu)椴豢梢姇r調用, 這個跟onPause的區(qū)別在于如果啟動的是一個對話框, 則onPause會執(zhí)行, onStop不會執(zhí)行
    • onDestroy() 活動被銷毀時調用
    • onRestart() 活動由停止狀態(tài)變?yōu)檫\行狀態(tài)時調用
完整生存期

可見生存期

前臺生存期

活動的生命周期
  1. 活動回收的處理
    前面的生命周期可以看到, 如果活動進入了停止狀態(tài), 是有可能被系統(tǒng)回收的. 那么這時候如果用戶按back鍵返回一個已經(jīng)被回收的活動, 這時候系統(tǒng)不會執(zhí)行onRestart()方法, 而是會執(zhí)行onCreate()方法重新創(chuàng)建一次.
    但是活動是可能存在輸入狀態(tài)的, 比如文本框中的輸入文字, 在系統(tǒng)回收了該活動后, 重新創(chuàng)建出來的活動如果沒有了用戶輸入的內容, 是會嚴重影響用戶體驗的. 所以Android提供了一個onSaveInstanceState(Bundle outState)方法, 用于在系統(tǒng)回收活動的時候調用該方法保存用戶輸入. 代碼如下
outState.putString("data_key", "helloworld");

現(xiàn)在我們再看onCreate方法, 注意到該方法中有一個參數(shù)onCreate(Bundle savedInstanceState), 此時我們只需要通過這個bundle恢復之前所保存的數(shù)據(jù)即可

if (savedInstanceState != null) {
    String data = savedInstanceState.getString("data_key");
}
  1. 活動的啟動模式
    活動由幾種啟動模式, 可以在AndroidManifest.xml中通過給<activity>標簽指定android:launchMode屬性來選中啟動模式
    • standard
      standard是活動默認的啟動模式, 在不進行指定的情況下, 所有活動都是使用這個模式. 在standard模式下, 每當啟動一個新的活動, 它就會在返回棧中入棧, 并處于棧頂?shù)奈恢? 系統(tǒng)并不關注活動是否在返回棧中已存在.
    • singleTop
      在singleTop模式下, 在啟動活動時系統(tǒng)會判斷返回棧的棧頂是否是該活動, 如果是則認為可以直接使用它. 不會再創(chuàng)建活動實例.
    • singleTask
      在singleTask模式下, 在啟動活動時系統(tǒng)會首先在返回棧中檢查是否已經(jīng)存在該活動的實例, 如果發(fā)現(xiàn)已存在則直接使用該實例, 并且把該活動之上的所有活動都出棧, 銷毀. 如果沒有則創(chuàng)建一個新的活動實例.
    • singleInstance
      指定為singleInstance模式的活動會啟用一個新的返回棧來管理這個活動. 一般用于在與其他的程序共享一個活動的實例時.

UI

常用控件

因為篇幅有限, 加上控件的使用大同小異, 就不詳細寫控件的使用了, 只寫常用控件的特性和應用場景, 目的就是讓大家知道有這么個東西, 能解決什么樣的問題, 在關鍵時刻的時候尋找谷哥的幫助時知道用什么關鍵詞.

  1. TextView 顯示文本信息
  2. Button 按鈕
  3. EditText 輸入框
  4. ImageView 圖片控件
  5. ProgressBar 進度條
  6. AlertDialog 對話框
  7. ProgressDialog 進度條對話框
  8. RecyclerView 滾動控件
基本布局

UI 就是布局加控件的組合 -- 魯迅.

所以我們接下來還要了解一下基本布局

  1. 線性布局 LinearLayout, 如名所示, 這個布局會將它所包含的控件在線性上依次排列.
  2. 相對布局 RelativeLayout, 它可以通過相對定位的方式讓控件出現(xiàn)在布局的任何地方.
  3. 幀布局 FrameLayout, 這種布局沒有方便的定位方式, 所有的控件都會默認擺放在布局的左上角.
  4. 百分比布局 PercentFrameLayout 和 PercentRelativeLayout

廣播

  1. 廣播機制簡介
    Android中的每個應用都可以對自己感興趣的廣播進行注冊, 這樣該程序就只會接收到自己所關心的廣播內容. 這些廣播可能是來自于系統(tǒng)的, 也可能是來自于其他應用程序的.

    • 標準廣播 是一種完全異步執(zhí)行的廣播, 在廣播發(fā)出之后, 所有的廣播接收器幾乎都會在同一時刻接收到這條廣播消息
    • 有序廣播 則是一種同步執(zhí)行的廣播, 在廣播發(fā)出之后, 同一時刻只會有一個廣播接收器可以接收到這條消息. 當這條廣播接受器的邏輯處理完成后, 廣播才會繼續(xù)傳遞, 因此前面的廣播接收器可以截斷正在傳遞的廣播, 這樣后面的廣播接收器就無法收到廣播消息了.
  2. 接收系統(tǒng)廣播

    • 動態(tài)注冊監(jiān)聽網(wǎng)絡變化, 先在AndroidManifest.xml中加入<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" ></uses-permission>
private IntentFilter intentFilter;
    private NetworkChangeReceiver networkChangeReceiver;
    @Override
    public void onCreate(Bundle saveedInstanceState) {
        super.onCreate(saveedInstanceState);
        setContentView(R.layout.main_activity);
        intentFilter = new IntentFilter();
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        networkChangeReceiver = new NetworkChangeReceiver();
        registerReceiver(networkChangeReceiver, intentFilter);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(networkChangeReceiver);
    }
    
    class NetworkChangeReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            
        }
    }
  • 靜態(tài)注冊實現(xiàn)開機啟動
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" ></uses-permission>
<receiver 
        android:name=".BroadcastReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
</receiver>
  1. 發(fā)送廣播
    發(fā)廣播的方法跟啟動活動的方法很相似
                Intent intent = new Intent("com.zzy.broadcast.mybroadcast");
                sendBoardcast(intent);

發(fā)送有序廣播的方法跟發(fā)送標準廣播的方法只需要修改一點sendOrderedBoardcast(intent, null); 這里的第二個參數(shù)是一個與權限有關的字符串.

  1. 發(fā)送本地廣播
    前面我們發(fā)送和接收的廣播全部屬于系統(tǒng)全局廣播, 這樣很容易引起安全性問題, 為了能夠解決安全性問題, android引入了一套本地廣播體系, 本地廣播只能夠在應用程序的內部進行傳遞, 并且廣播接收器也只能接收來自本應用程序發(fā)出的廣播. 本地廣播的操作和全局廣播基本是一樣的, 只是通過LocalBroadcastManager進行一層管理.
        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
        localBroadcastManager.sendBroadcast(intent);
        localBroadcastManager.registerReceiver(localReceiver, intentFilter);

數(shù)據(jù)持久化

  1. 文件存儲

多媒體

通知
                NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
                //NotificationChannel channelbody = new NotificationChannel(channel,"消息推送",NotificationManager.IMPORTANCE_DEFAULT);
                String channelid = "channelid";
                String channelname = "channelname";
                Notification notification;
                Intent intent = new Intent(MainActivity.this, FruitRecycleView.class);
                PendingIntent pendingIntent = PendingIntent.getActivity(MainActivity.this, 0, intent, 0); //設置點擊通知的響應
                int importance = NotificationManager.IMPORTANCE_LOW;
                NotificationChannel mChannel = manager.getNotificationChannel(channelid);;
                if (mChannel == null) {
                    mChannel = new NotificationChannel(channelid, channelname, importance);
                    mChannel.setDescription("My Channel");
                    // 設置通知出現(xiàn)時的閃燈(如果 android 設備支持的話)
                    mChannel.enableLights(true);
                    mChannel.setLightColor(Color.RED);
                    // 設置通知出現(xiàn)時的震動(如果 android 設備支持的話)
                    mChannel.enableVibration(true);
                    mChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});

                    manager.createNotificationChannel(mChannel);
                }
                notification = new NotificationCompat.Builder(MainActivity.this, channelid )
                        .setContentTitle("This is Title")
                        .setContentText("This is content Text")
                        .setWhen(System.currentTimeMillis())
                        .setSmallIcon(R.drawable.logo)
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.icon128))
                        .setAutoCancel(true)
                        .setContentIntent(pendingIntent)
                        .build();

                manager.notify(1, notification);
從相冊查看相片
if (ContextCompat.checkSelfPermission(cameraActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "onClick: checkpermission failed");
                    ActivityCompat.requestPermissions(cameraActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
                } else {
                    Log.d(TAG, "onClick: start image select");
                    Intent intent = new Intent("android.intent.action.GET_CONTENT");
                    intent.setType("image/*");
                    startActivityForResult(intent, 2);
                }
調用相機
                File file = new File(getExternalCacheDir(), "image.jpg");
                try {
                    if(file.exists()) {
                        file.delete();
                    }
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                // 第二個參數(shù)可以是任意唯一的字符串
                imageUri = FileProvider.getUriForFile(cameraActivity.this, "com.example.cameratest.provider", file);
                Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                startActivityForResult(intent, 1);

服務

Android多線程

和其他的gui庫一樣, android的UI也是線程不安全的, 也就是說, 如果想要更新UI元素, 就必須在主線程中進行, 否則就會出現(xiàn)異常.
因此, android提供了一套異步消息處理機制, 解決了線程中的通信問題.
寫在UI線程中

    private Handler handler = new Handler(){
          @Override
          public void handleMessage(Message message) {
              switch (message.what) {
                  case 1:
                      String txt = (String)message.obj;
                      text.setText(txt);
                      break;
              }
          }
    };

寫在子線程中

Message message = new Message();
message.obj = "helloworld";
handle.sendMessage(message);
使用AsyncTask

android還提供了另外一些工具以方便在子進程中對UI進行操作.

服務

跟activity類似, 服務也存在onCreate, onDestroy方法, 服務主要用于執(zhí)行一些比較耗時的工作. 另外Service并不是運行在單獨線程中,而是主線程中。所以盡量要避免一些ANR(Application Not Responding)的操作。

public class MyService extends Service {
    String TAG = "MyService";
    public MyService() {
    }

    @Override
    public void onCreate(){
        super.onCreate();
        Log.d("MyService", "onCreate execute");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId){
        Log.d("MyService", "onStartCommand execute");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy(){
        super.onDestroy();
        Log.d("MyService", "onDestroy execute");
    }
}
活動與服務通信

為了讓活動與服務通信, 需要借助onBind接口, 通過這個接口, 活動可以調用服務的方法獲取狀態(tài), 以進行ui更新操作, 比如說在迅雷中的下載操作需要通過服務來進行, 然而為了實時更新進度條, 這就需要活動通過onBind接口實時獲取到進度之后更新進度條顯示.

    public MyBinder myBinder = new MyBinder();
    class MyBinder extends Binder{

        public void startBind(){
            Log.d("MyService", "startBind execute");
        }

        public int getProgress() {
            Log.d(TAG, "getProgress: execute");
            return 0;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        Log.d("MyService", "onBind execute");
        return myBinder;
    }

    @Override
    public boolean onUnbind(Intent intent){
        Log.d("MyService", "onUnbind execute");
        return true;
    }

其中onBind和onUnbind方法是用于在綁定和解除綁定服務的時候調用的, 當活動與服務綁定之后, 就可以調用服務中的方法了.

Button bindService= (Button) findViewById(R.id.bindService);
        bindService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, MyService.class);
                bindService(intent, connection, BIND_AUTO_CREATE);
            }
        });

        Button unbindService= (Button) findViewById(R.id.unbindService);
        unbindService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                unbindService(connection);
            }
        });

private MyService.MyBinder myBinder ;
private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            myBinder = (MyService.MyBinder)iBinder;
            myBinder.startBind();
            myBinder.getProgress();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            
        }
    };
服務的生命周期

服務與活動的生命周期有一點重要的區(qū)別, 在于服務存在一個綁定的方法.

  1. onCreate() 創(chuàng)建時調用
  2. onStartCommand() 啟動服務時調用
  3. onBind() 綁定服務時調用, 只要調用方和服務之間的連接沒有斷開, 服務就會一直保持運行狀態(tài). 如果在之前沒有調用過startService()方法, 則還會先調用服務的onCreate()方法
  4. onUnBind() 取消綁定時調用, 如果是調用onBind()啟動的服務, 并且沒有調用過startService()方法, 則此時還會銷毀服務.
  5. onDestroy() 銷毀時調用, 注意如果一個服務既調用了startService()也調用了bingService()方法, 則此時必須要同時調用stopService()和unBindService()方法才會銷毀.

使用前臺服務

當系統(tǒng)內存不足時, 還是可能會回收掉后臺運行的服務, 如果希望服務可以一直保持運行狀態(tài)而不會因為系統(tǒng)內存不足而被回收掉, 可以使用前臺服務.
前臺服務和普通服務的區(qū)別在于前臺服務會一直有一個正在運行的圖標在系統(tǒng)的狀態(tài)欄顯示. 比如說音樂播放器的正在播放狀態(tài).

// 在service的oncreate方法中執(zhí)行
Intent intent = new Intent(MainActivity.this, FruitRecycleView.class);
PendingIntent pendingIntent = PendingIntent.getActivity(MainActivity.this, 0, intent, 0); //設置點擊通知的響應
Notification notification = new NotificationCompat.Builder(this)
                        .setContentTitle("This is Title")
                        .setContentText("This is content Text")
                        .setWhen(System.currentTimeMillis())
                        .setSmallIcon(R.drawable.logo)
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.icon128))
                        .setContentIntent(pendingIntent)
                        .build();
startForceground(1, notification);
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容