android 版本更新適配8.0,解決8.0手機沒法更新自動安裝apk

隨着android 7.0的普及android 8.0的也逐漸流行起來,那麼google對權限方面又有了新的修改。並且我發如今android8.0中除了一些bug,好比說:在小米6(Android 8.0系統)中沒法自動安裝的問題,那麼以前作的更新系統就要稍微調整下。android

        那根據android 8.0咱們從新理一下更新的思路:網絡

       一、寫一個接口調用是否有更新,能夠讀取版本號後臺判斷是否有更新,也可拉去最新信息,app內部判斷:app

       二、建立網絡監聽,監聽網絡變化,在application中初始化,dom

       三、建立更新service,根據service的機制,咱們能夠建立一個service不銷燬,調用startService方法,service不管你建立多少次onCreate方法只會執行一次(當你沒有stop的時候),而onStartCommand能夠屢次調用ide

       四、調用更新接口,在回調中判斷是否須要更新,若是須要更新就建立更新BroadcastReceiver廣播,用來通知更新的開始,進度更新,結束,和各類失敗的狀況,而後建立更新提示dialog(這個不用我說了吧),爲了防止更新按鈕屢次點擊和重複下載的問題,我使用了三個全局靜態變量來控制工具

    public static boolean hasNewAppVersion = false;//是否有新版本
    public static boolean isDownLoadApk = false;//apk是否正在下載
    public static boolean isSuccessRequestUpdate = false;//更新請求是否成功了網站

       5.點擊更新彈框的,更新按鈕,建立更新下載的service,注意下載須要在子線程中作。同時根據下載的開始,下載進度,或者結束,失敗等狀況利用廣播器進行通知。ui

       六、下載完成調用安裝,這裏必定要注意,android O更新了未知來源的權限,須要在配置文件中添加配置this

    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />google

那麼接下來咱們一步一步講解

1、接口調用是否有更新,返回數據以下大體以下:

                

{
"status":true,
"code":200,
"data":{
"VersionName":"1.1.8",
"VersionCode":19,
"AppUrl":"https://********/appfile/*****.apk",//更新地址
"Content":"部分BUG修復",//提示內容
"Status":1,
"isMustUpdate":true//是否強制更新
},
"msg":"返回成功"
}
2、網絡狀態監聽

      建立網絡狀態變化廣播器

/**
 * Created by CherishTang on 2016/12/29.
 * 自定義廣播接收器,監聽網絡狀態是否發生改變
 */
public class NetworkStateReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (NetworkStateUtil.isNetWorkConnected(context) && NetworkStateUtil.isWifiConnected(context)) {
            WifiInfo wifiInfo = NetworkStateUtil.getWifiInfo(context);
//            MThoast.showShort(context, "已鏈接到"+(wifiInfo.getSSID()==null?"wifi":wifiInfo.getSSID())+"網絡");
            processCustomMessage(context, 1);
        } else if (NetworkStateUtil.isNetWorkConnected(context) && NetworkStateUtil.isMobileConnected(context)) {
//            MThoast.showShort(context, "已鏈接到數據流量網絡");
            processCustomMessage(context, 2);
        } else {
            StaticUtil.isDownLoadApk = false;
            processCustomMessage(context, 3);
//            MThoast.showShort(context, "已進入無網絡次元,請檢查網絡設置!");
        }
    }

    //send msg to MainActivity
    private void processCustomMessage(Context context, Integer message) {
//        Intent mIntent = new Intent(BaseActivity.ACTION_INTENT_RECEIVER_MESSAGE);
//        mIntent.putExtra("message", message);
//        context.sendBroadcast(mIntent);
        if (networkChangeListener != null) {
            networkChangeListener.networkChanger(message);
        }
    }

    private NetworkChangeListener networkChangeListener;

    public void setNetworkChangeListener(NetworkChangeListener networkChangeListener) {
        this.networkChangeListener = networkChangeListener;
    }
}
添加網絡狀態變化監聽權限

<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
爲了防止重複註冊咱們再application中註冊廣播


public final static String ACTION_INTENT_RECEIVER ="android.net.conn.CONNECTIVITY_CHANGE";
public final static String ACTION_INTENT_RECEIVER_8 = "android.net.wifi.SCAN_RESULTS";


/**
* 註冊廣播,在onCreate中調用
*/
private void initNetWorkReceiver() {
intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_INTENT_RECEIVER);
intentFilter.addAction(ACTION_INTENT_RECEIVER_8);
networkChangeReceiver = new NetworkStateReceiver();
registerReceiver(networkChangeReceiver, intentFilter);
}

/**
* 回收廣播
*/
@Override
public void onTerminate() {
super.onTerminate();
if (networkChangeReceiver != null)
unregisterReceiver(networkChangeReceiver);
}

/**
* 獲取NetworkStateReceiver 實例
*/

public NetworkStateReceiver getNetworkChangeReceiver(){
return networkChangeReceiver;
}
3、建立更新service

       在你的MainActivity中開啓更新service,同時監聽網絡變化狀態,在onStartConmmand中檢查給更新

4、調用接口檢查更新

@Override
public int onStartCommand(Intent intent, int flags, int startId) {


checkVersionUpdate();//檢查更新網絡請求

//獲取網絡狀態變化廣播器
NetworkStateReceiver networkStateReceiver =
MyApplication.getInstance().getNetworkChangeReceiver();

//若是廣播器不爲空,在添加網絡變化監聽回調
if (networkStateReceiver != null)
networkStateReceiver.setNetworkChangeListener(this);

return super.onStartCommand(intent, flags, startId);
}

/**
* 註冊更新BroadcastReceiver
*
* @param versionMessage 更新數據
*/
public void updateCheck(VersionMessage versionMessage) {
StaticUtil.hasNewAppVersion = true;

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(DownloadApkService.ACTION_START);
intentFilter.addAction(DownloadApkService.ACTION_UPDATE);
intentFilter.addAction(DownloadApkService.ACTION_FINISHED);
intentFilter.addAction(DownloadApkService.ACTION_CANCEL);
intentFilter.addAction(DownloadApkService.ACTION_ERROR);
intentFilter.addAction(DownloadApkService.ACTION_REDIRECT_ERROR);

downloadApkReceiver = new DownloadApkReceiver();//建立更新下載廣播器
registerReceiver(downloadApkReceiver, intentFilter);
//獲取當前activity
Activity mContext = AppManager.getAppManager().currentActivity();

//若是程序在前臺,而且當前activity沒有銷燬的話,建立更新彈框,
//我封裝在UpdateManger方法中了
if (AppManager.isForeground(this) && mContext != null) {
UpdateManger.getInstance().checkUpdateInfo(mContext,
versionMessage.getAppUrl(), versionMessage.getContent(),
versionMessage.getVersionName(), versionMessage.getMustUpdate());
}
}
5、點擊更新按鈕下載新版app

首先判斷任務是否正在下載,避免重複下載

if (StaticUtil.isDownLoadApk) {
MThoast.showShort(mContext, "已存在下載任務,請勿重複下載");
return;
}
檢查並動態獲取文件的讀取權限

if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
//申請WRITE_EXTERNAL_STORAGE權限
ActivityCompat.requestPermissions(mContext, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
0x01);
return;
}
建立更新service並關閉更新提示框

if (customDialog != null && customDialog.isShowing())
customDialog.dismiss();
Intent intent = new Intent(mContext, DownloadApkService.class);
intent.setAction(DownloadApkService.ACTION_START);
intent.putExtra("id", 0);
intent.putExtra("url", apkUrl);
intent.putExtra("name", "保存到本地的app名稱");
mContext.startService(intent);
更新下載service代碼:

/**
* Created by CherishTang on 2017/10/17.
* 更新app服務
*/

public class DownloadApkService extends Service {

public static final String ACTION_START = "ACTION_START";
public static final String ACTION_UPDATE = "ACTION_UPDATE";
public static final String ACTION_FINISHED = "ACTION_FINISHED";
public static final String ACTION_CANCEL = "ACTION_CANCEL";
public static final String ACTION_ERROR = "ACTION_ERROR";
public static final String ACTION_REDIRECT_ERROR = "ACTION_REDIRECT_ERROR";
public static final String HIDE_DIALOG = "HIDE_DIALOG";

// 文件的保存路徑
public static final String path = Environment.getExternalStorageDirectory().getAbsolutePath() +
File.separator + StaticUtil.ROOTFILEPATH + File.separator + "download" + File.separator;

@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
if (ACTION_START.equals(intent.getAction())) {
new DownLoadApkThread(intent.getIntExtra("id", 0)
, intent.getStringExtra("url"), intent.getStringExtra("name")).start();
}
}
return super.onStartCommand(intent, flags, startId);
}


public class DownLoadApkThread extends Thread {

private int id;
private String downloadUrl;
private String fileName;

public DownLoadApkThread(int id, String downloadUrl, String fileName) {
this.id = id;
this.downloadUrl = downloadUrl;
this.fileName = fileName;

}

@Override
public void run() {
HttpURLConnection connLength = null;
HttpURLConnection connFile = null;
RandomAccessFile randomAccessFile = null;
InputStream inputStream = null;
URL url = null;
try {
url = new URL(downloadUrl);
//獲取apk文件長度
connLength = (HttpURLConnection) url.openConnection();
connLength.setConnectTimeout(5000);
connLength.setRequestMethod("GET");
int code = connLength.getResponseCode();
int length = 0;
if (code == HttpURLConnection.HTTP_OK) {
length = connLength.getContentLength();
} else if (code == 301) {

sendBroadcast(new Intent().setAction(ACTION_REDIRECT_ERROR));
return;

} else {
sendBroadcast(new Intent().setAction(ACTION_ERROR));
return;
}

//判斷文件是否存在,不存在則建立
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, fileName);
randomAccessFile = new RandomAccessFile(file, "rwd");
randomAccessFile.setLength(length);

//下載文件
connFile = (HttpURLConnection) url.openConnection();
connFile.setConnectTimeout(5000);
connFile.setRequestMethod("GET");
connFile.setRequestProperty("Range", "bytes=" + 0 + "-" + length);
code = connFile.getResponseCode();

//顯示通知欄進度條
Intent intent = new Intent();
intent.setAction(ACTION_START);
intent.putExtra("id", id);
sendBroadcast(intent);

if (code == HttpURLConnection.HTTP_PARTIAL) {
inputStream = connFile.getInputStream();
int finished = 0;
byte[] bytes = new byte[1024 * 1024];
int len = -1;
long time = System.currentTimeMillis();
while ((len = inputStream.read(bytes)) != -1) {
//文件寫入
randomAccessFile.write(bytes, 0, len);
//更新通知欄進度條
finished += len;
if (System.currentTimeMillis() - time > 1000) {
time = System.currentTimeMillis();
intent.setAction(ACTION_UPDATE);
int pro = (int) (((float) finished / length) * 100);
intent.putExtra("finished", pro);
sendBroadcast(intent);
}
}
}
//關閉通知欄
intent.setAction(ACTION_FINISHED);
sendBroadcast(intent);

} catch (MalformedURLException e) {
sendBroadcast(new Intent().setAction(ACTION_ERROR));
e.printStackTrace();
} catch (IOException e) {
sendBroadcast(new Intent().setAction(ACTION_ERROR));
e.printStackTrace();
} finally {
if (connLength != null) {
connLength.disconnect();
}
if (connFile != null) {
connFile.disconnect();
}
try {
if (inputStream != null) {
inputStream.close();
}
if (randomAccessFile != null) {
randomAccessFile.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
super.run();
}
}
}
更新下載通知廣播:


/**
* Created by 方舟 on 2017/10/17.
* 更新下載廣播接收器
*/

public class DownloadApkReceiver extends BroadcastReceiver {

private UpdateNotificationUtil mNotificationUtil;

@Override
public void onReceive(Context context, Intent intent) {
mNotificationUtil = new UpdateNotificationUtil(context);

if (DownloadApkService.ACTION_START.equals(intent.getAction())) {
StaticUtil.isDownLoadApk = true;//單例下載,防止多任務進行
// 下載開始的時候啓動通知欄
mNotificationUtil.showNotification(intent.getIntExtra("id", 0));
} else if (DownloadApkService.ACTION_UPDATE.equals(intent.getAction())) {
// 更新進度條
mNotificationUtil.updateNotification(intent.getIntExtra("id",0), intent.getIntExtra("finished", 0));
} else if (DownloadApkService.ACTION_FINISHED.equals(intent.getAction())) {
StaticUtil.isDownLoadApk = false;//變動未任務未下載
mNotificationUtil.cancelNotification(intent.getIntExtra("id", 0));// 下載結束後取消通知
UpdateManger.installApk(context, new File(DownloadApkService.path + StaticUtil.apkName));
} else if (DownloadApkService.ACTION_CANCEL.equals(intent.getAction())) {
StaticUtil.isDownLoadApk = false;//變動未任務未下載
mNotificationUtil.cancelNotification(intent.getIntExtra("id", 0));// 下載結束後取消通知
} else if (DownloadApkService.ACTION_ERROR.equals(intent.getAction())) {
MThoast.showShort(context, "讀取文件失敗,請前往官方網站掃碼下載最新版本!");
}else if (DownloadApkService.ACTION_REDIRECT_ERROR.equals(intent.getAction())) {
MThoast.showShort(context, "下載地址重定向出現錯誤,請稍後再試!");
}
}
}
因爲android 8.0添加了NotificationChannel的概念所以這個UpdateNotificationUtil工具類能夠根據新的方式本身寫,我貼一個僅供參考其中

StaticUtil.updateVerisonChannelId,StaticUtil.updateVerisonChannelName,兩個靜態變量最好不要重複

public static final String updateVerisonChannelId = "包名.updateVersionApp";
public static final String updateVerisonChannelName = "版本更新";
/**
* Created by CherishTang on 2017/10/13.
* 更新通知
*/
public class UpdateNotificationUtil {

private Context mContext;
private NotificationManager mManager;
private NotificationCompat.Builder mBuilder;

public UpdateNotificationUtil(Context context) {
this.mContext = context;
mManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationUtils.createNotificationChannel(
false,false,
StaticUtil.updateVerisonChannelId,
StaticUtil.updateVerisonChannelName,
NotificationManager.IMPORTANCE_DEFAULT);
}
mBuilder = new NotificationCompat.Builder(context, StaticUtil.updateVerisonChannelId);
}

/**
* 顯示通知欄
*
* @param id
*/
public void showNotification(final int id) {

mBuilder.setTicker("正在下載");//Ticker是狀態欄顯示的提示
mBuilder.setOngoing(true);
mBuilder.setContentTitle("正在下載最新版本");
mBuilder.setProgress(100, 0, false);
mBuilder.setContentText(0 + "%");
mBuilder.setSmallIcon(R.mipmap.icon_launcher);
mBuilder.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.icon_launcher));//通知欄的大圖標
Intent msgIntent = new Intent();
msgIntent.setClass(mContext, MainActivity.class);
msgIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 100, msgIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pendingIntent);//點擊跳轉
mManager.notify(id, mBuilder.build());
}

/**
* 取消通知欄通知
*/
public void cancelNotification(int id) {
mManager.cancel(id);
}

/**
* 更新通知欄進度條
*
* @param id 獲取Notification的id
* @param progress 獲取的進度
*/
public void updateNotification(int id, int progress) {
if (mBuilder != null) {
mBuilder.setTicker("開始下載");//Ticker是狀態欄顯示的提示
mBuilder.setOngoing(true);
mBuilder.setContentTitle("app名稱");
mBuilder.setSmallIcon(R.mipmap.icon_launcher);
mBuilder.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.icon_launcher));//通知欄的大圖標
mBuilder.setProgress(100, progress, false);
mBuilder.setContentText(progress + "%");
mManager.notify(id, mBuilder.build());
}
}
}
/**
* Created by CherishTang on 2018/7/19.
* 通知欄工具類
*/

public class NotificationUtils {
private static NotificationManager mManager;
private static NotificationCompat.Builder notification;
private String contentText, contentTitle, channelId;
private boolean autoCancel = true;
private static Application application;

public static void init(Application application) {
NotificationUtils.application = application;
mManager = (NotificationManager) application.getSystemService(NOTIFICATION_SERVICE);
}

public NotificationUtils Builder(String channelId) {
if (notification == null) {
notification = new NotificationCompat.Builder(application, channelId == null ? "chat" : channelId);
} else {
notification.setChannelId(channelId);
}
this.channelId = channelId;
return this;
}

public static NotificationManager getManager() {
return mManager;
}

public static NotificationCompat.Builder getNotification() {
return notification;
}

@TargetApi(Build.VERSION_CODES.O)
public static void createNotificationChannel(boolean isVibrate,
boolean hasSound,
String channelId,
String channelName,
int importance) {
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
NotificationManager notificationManager = (NotificationManager) application.getSystemService(
NOTIFICATION_SERVICE);
channel.enableVibration(isVibrate);
channel.enableLights(true);
if (!hasSound)
channel.setSound(null, null);
if (notificationManager != null)
notificationManager.createNotificationChannel(channel);
}

public NotificationUtils setChannelId(String channelId) {
notification.setChannelId(channelId);
this.channelId = channelId;
return this;
}

public NotificationUtils setContentText(String contentText) {
notification.setContentText(contentText);
this.contentText = contentText;
return this;
}

public NotificationUtils setContentTitle(String contentTitle) {
notification.setContentTitle(contentTitle);
this.contentTitle = contentTitle;
return this;
}

public NotificationUtils setAutoCancel(boolean autoCancel) {
notification.setAutoCancel(autoCancel);
this.autoCancel = autoCancel;
return this;
}

public NotificationUtils notifyMessage(int id) {
if (mManager != null) {
notification.setContentTitle(contentTitle)
.setContentText(contentText)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.icon_launcher)
.setLargeIcon(BitmapFactory.decodeResource(application.getResources(), R.mipmap.icon_launcher))
.setAutoCancel(autoCancel)
.build();
mManager.notify(id, notification.build());
}
return this;
}

}
6、下載完成後啓動自動安裝

首先注意在Android O新增權限,必須添加,最後調用安裝方法

<!--適配android 8.0-->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
public static void installApk(Context mContext, File apkFile) {
try{
if (!apkFile.exists()) {
return;
}
Intent i = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= 24) { //適配安卓7.0
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK); //添加這一句表示對目標應用臨時受權該Uri所表明的文件
Uri apkFileUri = FileProvider.getUriForFile(mContext.getApplicationContext(),
mContext.getPackageName() + ".FileProvider", apkFile);
i.setDataAndType(apkFileUri, "application/vnd.android.package-archive");
} else {
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setDataAndType(Uri.parse("file://" + apkFile.toString()),
"application/vnd.android.package-archive");// File.toString()會返
迴路徑信息
}
mContext.startActivity(i);
}catch (Exception e){
e.printStackTrace();
MThoast.showShort(mContext,"自動安裝失敗,請嘗試手動安裝!");
}
}
到這裏版本更新就行了,你們有問題能夠留言。

demo地址:https://download.csdn.net/download/fzkf9225/10679015

我作了兩個下載進度條,通知欄和dialog,我以爲通常用通知欄就能夠了,dialog最好不用若是關閉下載進度的dialog彈框的話,註銷相關DownloadProgressDialog類的相關代碼便可--------------------- 做者:青穗CherishTang 來源:CSDN 原文:https://blog.csdn.net/fzkf9225/article/details/80969439 版權聲明:本文爲博主原創文章,轉載請附上博文連接!

相關文章
相關標籤/搜索