Android 版本更新------後臺服務和前臺通知欄實現
阿新 • • 發佈:2021-01-01
技術標籤:我的Android之路androidappapkjava
前言:最近公司專案版本需要把以前的版本更新程式碼更新一下,換成Service和前臺通知欄形式的,所以在此記錄一下。
目錄
2.建立Service開啟前臺通知欄,並且執行下載任務,將下載進度通知到通知欄,下載完成後進行安裝
2.開啟一個透明InstallApkActivity執行安裝Apk操作
3.注意事項:因為24版本之後讀取檔案需要配置FileProvider,不然無法讀取到apk檔案
在res目錄下建一個xml資料夾,然後建立file_path.xml檔案
1.檢測版本更新(省略)
這一步一般是通過後臺介面對比當前APP版本是否低於伺服器版本,從而獲取最新版本的下載連結。這一步比較簡單所以省略了。
2.建立Service開啟前臺通知欄,並且執行下載任務,將下載進度通知到通知欄,下載完成後進行安裝
完整的Service程式碼
package com.qtz.online.service; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.IBinder; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; import com.qtz.online.R; import com.qtz.online.common.AppService; import com.qtz.online.event_bus.DownloadApkMessage; import com.qtz.online.mvp.activitys.InstallApkActivity; import com.qtz.online.network.callback.ServiceDownloadObserver; import com.qtz.online.network.client.DownLoadClient; import com.qtz.online.utils.LogUtil; import org.greenrobot.eventbus.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import okhttp3.ResponseBody; /** * @author Created by PengGuiChu on 2020/12/19 19:32. * @explain 版本更新服務 */ public class DownloadApkService extends Service { private static final String TAG=DownloadApkService.class.getSimpleName(); private String url;//下載 private String fileDir; private String fileName; private NotificationManager notificationManager; private NotificationCompat.Builder notificationBuilder; private static final String name="download_apk"; private static final String id="Download_APK"; private static final int startForegroundId=777; private int downLoadProgress; @Override public void onCreate() { super.onCreate(); notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationBuilder = getNotificationBuilder(notificationManager,getString(R.string.version_upgrade),getString(R.string.downloading,"0%")); notificationBuilder.setProgress(100,0,false); startForeground(startForegroundId,notificationBuilder.build()); // notificationManager.notify(2,notificationBuilder.build()); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (intent!=null){ url=intent.getStringExtra("url"); fileDir=intent.getStringExtra("fileDir"); fileName=intent.getStringExtra("fileName"); //該下載請求是我專案的下載請求,需要替換成你專案的下載檔案的請求 downloadFile(DownLoadClient.getService(AppService.class).download(url), new ServiceDownloadObserver<ResponseBody>(fileDir,fileName) { @Override public void onDownloadStart() { } @Override public void onDownloading(long progress, long total) { int down= (int) (progress*100/total); if (down!=downLoadProgress){ downLoadProgress=down; if (notificationBuilder!=null){ notificationBuilder.setContentText(getString(R.string.downloading,downLoadProgress+"%")); notificationBuilder.setProgress(100, downLoadProgress,false); } if (notificationManager!=null){ notificationManager.notify(startForegroundId,notificationBuilder.build()); } } } @Override public void onDownloadSuccess(ResponseBody responseBody, String filePath) { LogUtil.d(TAG,"下載完成,開啟一個空白Activity執行安裝視窗,保證使用者在任何地方都能開啟安裝步驟"); // EventBus.getDefault().post(new DownloadApkMessage(0,filePath)); Intent intent=new Intent(getApplicationContext(), InstallApkActivity.class); intent.putExtra("filePath",filePath); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); stopForeground(true); stopSelf(); } @Override public void onDownloadError(String msg) { LogUtil.d(TAG,"下載出錯"); EventBus.getDefault().post(new DownloadApkMessage(1,msg)); stopForeground(true); stopSelf(); } }); } return super.onStartCommand(intent, flags, startId); } private NotificationCompat.Builder getNotificationBuilder(NotificationManager notificationManager, String title, String message) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(DownloadApkService.id, DownloadApkService.name, NotificationManager.IMPORTANCE_LOW); //閃光燈 channel.enableLights(false); //是否允許震動 channel.enableVibration(false); //設定可繞過 請勿打擾模式 channel.setBypassDnd(false); channel.setSound(null,null); notificationManager.createNotificationChannel(channel); } NotificationCompat.Builder notification = new NotificationCompat.Builder(this, DownloadApkService.id); notification.build().flags= Notification.FLAG_ONGOING_EVENT; notification.setContentTitle(title); notification.setDefaults(NotificationCompat.DEFAULT_VIBRATE); notification.setContentText(message); notification.setSmallIcon(R.mipmap.ic_launcher); notification.setAutoCancel(false); notification.setCategory(Notification.CATEGORY_PROGRESS); return notification; } protected void downloadFile(Observable<ResponseBody> downloadObservable, final ServiceDownloadObserver<ResponseBody> downloadObserver) { downloadObservable.subscribeOn(Schedulers.io())//請求網路 在排程者的io執行緒 .observeOn(Schedulers.io()) //指定執行緒儲存檔案 .doOnNext(downloadObserver::saveFile) .observeOn(AndroidSchedulers.mainThread()) //在主執行緒中更新ui .subscribe(downloadObserver); } }
Service所用到的變數補充
<string name="version_upgrade">發現新版本</string>
<string name="downloading">下載中…%1$s</string>
2.開啟一個透明InstallApkActivity執行安裝Apk操作
Activity程式碼
package com.qtz.online.mvp.activitys; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import com.qtz.online.R; import com.qtz.online.utils.InstallApkUtil; import java.io.File; public class InstallApkActivity extends AppCompatActivity { private InstallApkUtil installApkUtil; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_install_apk); String filePath = getIntent().getStringExtra("filePath"); initInstallApkUtil(new File(filePath)); } private void initInstallApkUtil(File file) { if (installApkUtil == null) { installApkUtil = new InstallApkUtil(InstallApkActivity.this, file.getAbsolutePath()); } installApkUtil.installApk(); } //更具使用者授權回撥執行安裝操作 @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 101) { if (installApkUtil != null) { installApkUtil.installApk(); } }else if (requestCode==102){ finish(); } } }
工具類程式碼
package com.qtz.online.utils;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import com.qtz.online.BuildConfig;
import com.qtz.online.base.BaseActivity;
import com.qtz.online.view.ChoiceDialog;
import org.greenrobot.eventbus.EventBus;
import java.io.File;
/**
* @author Created by PengGuiChu on 2020/12/19 15:53.
* @explain
*/
public class InstallApkUtil {
private AppCompatActivity baseActivity;
private String apkPath;
private ChoiceDialog choiceDialog;
public InstallApkUtil(AppCompatActivity baseActivity, String apkPath) {
this.baseActivity = baseActivity;
this.apkPath=apkPath;
}
public void installApk() {
if (Build.VERSION.SDK_INT >= 26) {
//來判斷應用是否有許可權安裝apk
boolean installAllowed = baseActivity.getPackageManager().canRequestPackageInstalls();
//有許可權
if (installAllowed) {
//安裝apk
install();
} else {
//這裡彈出一個選擇對話方塊,提示使用者需要授權第三方安裝應用,需要自己實現
if (choiceDialog==null){
choiceDialog = new ChoiceDialog(baseActivity, new ChoiceDialog.ClauseDialogCallBack() {
@Override
public void onExit() {
baseActivity.finish();
}
@Override
public void onAgree() {
Uri packageURI = Uri.parse("package:" + baseActivity.getPackageName());
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
baseActivity.startActivityForResult(intent, 101);
}
}, "安裝應用需要開啟未知來源許可權,請去設定中開啟許可權", "許可權申請", "取消", "前往");
}
choiceDialog.show();
}
} else {
install();
}
}
public void install() {
if (Build.VERSION.SDK_INT >= 24) {
Uri apkUri = FileProvider.getUriForFile(baseActivity,
BuildConfig.APPLICATION_ID + ".myprovider", new File(apkPath));
Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
baseActivity.startActivityForResult(installIntent,102);
} else {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.parse("file://" + apkPath), "application/vnd.android.package-archive");
baseActivity.startActivityForResult(intent,102);
}
}
}
3.注意事項:因為24版本之後讀取檔案需要配置FileProvider,不然無法讀取到apk檔案
在res目錄下建一個xml資料夾,然後建立file_path.xml檔案
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-cache-path path="apk" name="."/>
</paths>
因為我是把apk下載在了Android/Data/包名/cache/apk目錄下所以這樣配置,這裡需要根據你的具體情況進行配置
<root-path/>
代表裝置的根目錄new File("/");<files-path/>
代表context.getFilesDir()<cache-path/>
代表context.getCacheDir()<external-path/>
代表Environment.getExternalStorageDirectory()<external-files-path>
代表context.getExternalFilesDirs()<external-cache-path>
代表getExternalCacheDirs()
AndroidManifest.xml配置
<provider
android:name=".utils.OnlineFileProvider"
android:authorities="${applicationId}.myprovider"
android:exported="false"
android:grantUriPermissions="true">
<!-- 元資料 -->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path" />
</provider>
OnlineFileProvider類程式碼
package com.qtz.online.utils;
import androidx.core.content.FileProvider;
/**
* @author Created by PengGuiChu on 2020/12/19 16:39.
* @explain
*/
public class OnlineFileProvider extends FileProvider {
}