android开辟新进程,远程服务的方式实现app升级

创建一个Service,用于下载apk文件,service下载的时候实时更新通知栏的下载进度,同时通过回调,把下载情况的回调传给调用者,实现远程服务下载功能

必须在Manifest中注册Service:


android:exported="true"
android:process=":update"
/>
public class DownloadService extends Service {

private static final int NOTIFY_ID = 0;
private static final String CHANNEL_ID = "app_update_id";
private static final CharSequence CHANNEL_NAME = "app_update_channel";
private static ServiceConnection connection;
private static Context context;
private NotificationManager mNotificationManager;
private DownloadBinder binder = new DownloadBinder();
private NotificationCompat.Builder mBuilder;
private static IDownloadCallback mCallBack = null;
public static void bindService(Context context, ServiceConnection connection) {
    DownloadService.context = context;
    DownloadService.connection = connection;
    Intent intent = new Intent(context, DownloadService.class);
    context.startService(intent);
    context.bindService(intent, connection, Context.BIND_AUTO_CREATE);

}
public static void unBindService(){
    if (context != null && connection != null){
        context.unbindService(connection);
        mCallBack = null;
        connection = null;
    }
}
@Override
public boolean onUnbind(Intent intent) {
    return super.onUnbind(intent);
}

@Override
public void onCreate() {
    super.onCreate();
    mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}

@Override
public IBinder onBind(Intent intent) {
    // 返回自定义的DownloadBinder实例
    return binder;
}

@Override
public void onDestroy() {
    mNotificationManager = null;
    super.onDestroy();
}

/**
 * 创建通知
 */
private void setUpNotification() {
    if (android.os.Build.VERSION.SDK_INT >=   android.os.Build.VERSION_CODES.O) {
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
        channel.enableVibration(false);
        channel.enableLights(false);
        mNotificationManager.createNotificationChannel(channel);
    mBuilder = new NotificationCompat.Builder(this);
    mBuilder.setContentTitle("开始下载")
            .setContentText("正在连接服务器")
            .setSmallIcon(R.drawable.lib_update_app_update_icon)
            .setLargeIcon(AppUpdateUtils.drawableToBitmap(AppUpdateUtils.getAppIcon(DownloadService.this)))
            .setOngoing(true)
            .setAutoCancel(true)
            .setWhen(System.currentTimeMillis());
    mNotificationManager.notify(NOTIFY_ID, mBuilder.build());
}

/**
 * 下载模块
 */
private void startDownload(UpdateAppBean updateApp,IDownloadCallback callback) {
    UpdateTask updateTask = new UpdateTask(new FileDownloadCallBack(callback));
    updateTask.execute(updateApp);
}

private void stop(String contentText) {
    if (mBuilder != null) {
        mBuilder.setContentTitle(AppUpdateUtils.getAppName(DownloadService.this))
                .setContentText(contentText);
        Notification notification = mBuilder.build();
        notification.flags = Notification.FLAG_AUTO_CANCEL;
        mNotificationManager.notify(NOTIFY_ID, notification);
    }
    close();
}

private void close() {
    stopSelf();
}

/**
 * DownloadBinder中定义了一些实用的方法
 *
 * @author user
 */
public class DownloadBinder extends IDownloadlInterface.Stub{
    @Override
    public void start(UpdateAppBean bean, IBinder iBinder) throws RemoteException {
        /**
         * 开始下载
         *
         */
        IDownloadCallback callback = IDownloadCallback.Stub.asInterface(iBinder);
        startDownload(bean,callback);
    }

    @Override
    public void stop(String msg) throws RemoteException {

    }

}

public class FileDownloadCallBack implements FileCallBack {
    int oldRate = 0;

    public FileDownloadCallBack(@Nullable IDownloadCallback callback) {
        super();
        mCallBack = callback;
    }

    @Override
    public void onBefore() {
        //初始化通知栏
        setUpNotification();
        if (mCallBack != null) {
            try {
                mCallBack.onStart();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onProgress(float progress, long total) {
        //做一下判断,防止自回调过于频繁,造成更新通知栏进度过于频繁,而出现卡顿的问题。
        int rate = Math.round(progress * 100);
        if (oldRate != rate) {
            if (mCallBack != null) {
                try {
                    mCallBack.setMax(total);
                    mCallBack.onProgress(progress, total);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            if (mBuilder != null) {
                // 创建一个数值格式化对象
                NumberFormat numberFormat = NumberFormat.getInstance();
                // 设置精确到小数点后2位
                numberFormat.setMaximumFractionDigits(2);
                String result = numberFormat.format((float) progress / (float) total * 100);
                mBuilder.setContentTitle("正在下载:" + AppUpdateUtils.getAppName(DownloadService.this))
                        .setContentText(result+"%")
                        .setProgress((int) total, (int) progress, false)
                        .setWhen(System.currentTimeMillis());
                Notification notification = mBuilder.build();
                notification.flags = Notification.FLAG_AUTO_CANCEL | Notification.FLAG_ONLY_ALERT_ONCE;
                mNotificationManager.notify(NOTIFY_ID, notification);
            }
            //重新赋值
            oldRate = rate;
        }

    }

    @Override
    public void onError(String error) {
        Toast.makeText(DownloadService.this, "更新新版本出错," + error, Toast.LENGTH_SHORT).show();
        //App前台运行
        if (mCallBack != null) {
            try {
                mCallBack.onError(error);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        try {
            mNotificationManager.cancel(NOTIFY_ID);
            close();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    @Override
    public void onResponse(String filePath) {
        try {
            if (AppUpdateUtils.isAppOnForeground(DownloadService.this) || mBuilder == null) {
                //App前台运行
                mNotificationManager.cancel(NOTIFY_ID);
                if (mCallBack != null) {
                    boolean temp = mCallBack.onInstallAppAndAppOnForeground(filePath);
                    mCallBack.onFinish(filePath);
                    if (!temp) {
                        AppUpdateUtils.installApp(DownloadService.this, filePath);
                    }
                } else {
                    AppUpdateUtils.installApp(DownloadService.this, filePath);
                }

            } else {
                //App后台运行, 点击通知栏安装
                //更新参数,注意flags要使用FLAG_UPDATE_CURRENT
                Intent installAppIntent = AppUpdateUtils.getInstallAppIntent(DownloadService.this,filePath);
                PendingIntent contentIntent = PendingIntent.getActivity(DownloadService.this, 0, installAppIntent, PendingIntent.FLAG_UPDATE_CURRENT);
                mBuilder.setContentIntent(contentIntent)
                        .setContentTitle(AppUpdateUtils.getAppName(DownloadService.this))
                        .setContentText("下载完成,请点击安装")
                        .setProgress(0, 0, false)
                        //                        .setAutoCancel(true)
                        .setDefaults((Notification.DEFAULT_ALL));
                Notification notification = mBuilder.build();
                notification.flags = Notification.FLAG_AUTO_CANCEL;
                mNotificationManager.notify(NOTIFY_ID, notification);
            }
            //下载完关闭
            close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close();
        }
    }
}

}

创建AIDL文件,android中跨进程通讯只能使用AIDL

AIDL文件中接口函数的参数只支持以下几种方式

1. Java 的原生类型

2. String 和CharSequence

3. List 和 Map ,List和Map 对象的元素必须是AIDL支持的数据类型; 以上三种类型都不需要导入(import)

4. AIDL 自动生成的接口 需要导入(import)

5. 实现android.os.Parcelable 接口的类. 需要导入(import)。

service中的binder类

interface IDownloadlInterface {

 void start(in UpdateAppBean bean,IBinder callback);

 void stop(String msg);

}

接口中start(in UpdateAppBean bean,IBinder callback);方法的第一个参数,是一个类UpdateAppBean.class必须实现Parcelable接口。而且必须在与UpdateAppBean.class同一个包下增加UpdateAppBean.aidl

UpdateAppBean.aidl文件内容:

package com.uadzd.update;//包名

parcelable UpdateAppBean;//类名

public class UpdateAppBean implements Parcelable {

private String newVersionName;
private String newVersionCode;
private String oldVersionCode;
private String downloadUrl;
private String updateMsg;

private String targetPath;
public UpdateAppBean(){

}
protected UpdateAppBean(Parcel in) {
    newVersionName = in.readString();
    newVersionCode = in.readString();
    oldVersionCode = in.readString();
    downloadUrl = in.readString();
    updateMsg = in.readString();
    targetPath= in.readString();
}

public String getNewVersionName() {
    return newVersionName;
}

public void setNewVersionName(String newVersionName) {
    this.newVersionName = newVersionName;
}

public String getNewVersionCode() {
    return newVersionCode;
}

public void setNewVersionCode(String newVersionCode) {
    this.newVersionCode = newVersionCode;
}

public String getOldVersionCode() {
    return oldVersionCode;
}

public void setOldVersionCode(String oldVersionCode) {
    this.oldVersionCode = oldVersionCode;
}

public String getDownloadUrl() {
    return downloadUrl;
}

public void setDownloadUrl(String downloadUrl) {
    this.downloadUrl = downloadUrl;
}

public String getUpdateMsg() {
    return updateMsg;
}

public void setUpdateMsg(String updateMsg) {
    this.updateMsg = updateMsg;
}

public String getTargetPath() {
    return targetPath;
}

public void setTargetPath(String targetPath) {
    this.targetPath = targetPath;
}

public static final Creator CREATOR = new Creator() {
    @Override
    public UpdateAppBean createFromParcel(Parcel in) {
        return new UpdateAppBean(in);
    }

    @Override
    public UpdateAppBean[] newArray(int size) {
        return new UpdateAppBean[size];
    }
};

@Override
public int describeContents() {
    return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(newVersionName);
    dest.writeString(newVersionCode);
    dest.writeString(oldVersionCode);
    dest.writeString(downloadUrl);
    dest.writeString(updateMsg);
    dest.writeString(targetPath);
}

}

接口中start(in UpdateAppBean bean,IBinder callback);方法的第二个参数是一个接口类型,使用接口类型作为参数(用于回调)的时候,接口文件必须定义为***.aidl,不能跟平时定义接口一样直接 ***.java,IDownloadCallback 为IBinder的基类,网上看了很多写法都是直接第二个参数为IDownloadCallback ,然后导入对应的包。start(in UpdateAppBean bean,IDownloadCallback callback);

这里有坑,一直在service拿到的回调是空,应该使用IBinder 最为参数,然后再通过

IDownloadCallback callback = IDownloadCallback.Stub.asInterface(iBinder);进行转换。

interface IDownloadCallback {

void onStart();

/**
 * 进度
 *
 * @param progress  进度 0.00 -1.00 ,总大小
 * @param totalSize 总大小 单位M
 */
void onProgress(float progress, long totalSize);

/**
 * 总大小
 *
 * @param totalSize 单位M
 */
void setMax(long totalSize);

/**
 * 下载完了
 *
 * @param filePath 下载的app
 * @return true :下载完自动跳到安装界面,false:则不进行安装
 */
boolean onFinish(String filePath);

/**
 * 下载异常
 *
 * @param msg 异常信息
 */
void onError(String msg);

/**
 * 当应用处于前台,准备执行安装程序时候的回调,
 *
 * @param filePath 当前安装包路径
 * @return false 默认 false ,当返回时 true 时,需要自己处理 ,前提条件是 onFinish 返回 false 。
 */
boolean onInstallAppAndAppOnForeground(String filePath);

}

FileCallback接口类,文件下载进度回调接口

public interface FileCallBack {

void onProgress(float progress, long total);

/**
 * 错误回调
 *
 * @param error 错误提示
 */
void onError(String error);

/**
 * 结果回调
 *
 * @param filePaht 下载好的文件路径
 */
void onResponse(String filePaht);

/**
 * 请求之前
 */
void onBefore();

}

下载工具类

package com.uadzd.update;

public class UpdateTask extends AsyncTask {

private DownloadService.FileDownloadCallBack fileDownloadCallBack;

public UpdateTask(DownloadService.FileDownloadCallBack fileDownloadCallBack) {

    this.fileDownloadCallBack = fileDownloadCallBack;
}

@Override
protected void onPreExecute() {
    if (fileDownloadCallBack != null){
        fileDownloadCallBack.onBefore();
    }
    super.onPreExecute();
}

@Override
protected String doInBackground(UpdateAppBean... params) {
    UpdateAppBean updateAppBean = params[0];
    HttpURLConnection conn = null;
    File file = null;
    String path = AppUpdateUtils.appIsDownloaded(updateAppBean);
    if (path != null){
        return path;
    }
    //如果相等的话表示当前的sdcard挂载在手机上并且是可用的
    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
        final OkHttpClient okHttpClient = new OkHttpClient();
        final Request request = new Request.Builder()
                .url(updateAppBean.getDownloadUrl())
                .build();
        Call call = okHttpClient.newCall(request);
        try {
            Response response = call.execute();
            long totalLen = response.body().contentLength();
            InputStream is = response.body().byteStream();
            //下载到本地的app名
            file = AppUpdateUtils.getAppFile(updateAppBean);
            FileOutputStream fos = new FileOutputStream(file);
            BufferedInputStream bis = new BufferedInputStream(is);
            byte[] buffer = new byte[1024];
            int len;
            int total = 0;
            while ((len = bis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
                total += len;
                //获取当前下载量
                publishProgress(total, (int) totalLen);
            }
            fos.close();
            bis.close();
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file.getAbsolutePath();
    }else {
        return null;
    }

}

@Override
protected void onProgressUpdate(Integer... values) {
    if (fileDownloadCallBack != null){
        fileDownloadCallBack.onProgress(values[0] / 1024 / 1024, values[1] / 1024 / 1024);
    }
    super.onProgressUpdate(values);
}

@Override
protected void onPostExecute(String filePaht) {
    if (filePaht != null) {
        if (fileDownloadCallBack != null){
            fileDownloadCallBack.onResponse(filePaht);
        }
    } else {
        if (fileDownloadCallBack != null){
            fileDownloadCallBack.onError("没有可用于下载的存储空间!");
        }
    }

}

}

调用远程服务的方法,与上面service不在同一个进程

private void startUpdate(final boolean isInBackground) {
    String updateUrl = "http://imtt.dd.qq.com/16891/E29FAA923B6DA21BEA23C2EC4C6F7BC9.apk?fsname=com.uadzd_2.0_6.apk&csr=1bbd";
    final UpdateAppBean updateAppBean = new UpdateAppBean();
    updateAppBean.setDownloadUrl(updateUrl);
    updateAppBean.setNewVersionName("1.1.4");
    DownloadService.bindService(this.getApplicationContext(), new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IDownloadlInterface iDownloadlInterface = IDownloadlInterface.Stub.asInterface(service);
            try {
                iDownloadlInterface.start(updateAppBean, new IDownloadCallback.Stub() {
                    @Override
                    public void onStart() throws RemoteException {
                        if (isInBackground){
                            goNext();
                        }else {
                            if (pd == null){
                                showProgressDialog();
                            }
                        }
                    }

                    @Override
                    public void onProgress(float progress, long totalSize) throws RemoteException {
                        NumberFormat numberFormat = NumberFormat.getInstance();
                        numberFormat.setMaximumFractionDigits(2);
                        String result = numberFormat.format((float) progress / (float) totalSize * 100);
                        Log.i("aaa", "result"+result);
                        if (!isInBackground){
                            if (pd == null){
                                showProgressDialog();
                            }else {
                                pd.setMax((int) totalSize);
                                pd.setProgress((int) progress);
                            }
                        }
                    }

                    @Override
                    public void setMax(long totalSize) throws RemoteException {

                    }

                    @Override
                    public boolean onFinish(String filePath) throws RemoteException {
                        Log.i("aaa", "onFinish:"+filePath);
                        if (pd != null ){
                            pd.dismiss();
                        }
                        return false;
                    }

                    @Override
                    public void onError(String msg) throws RemoteException {

                    }

                    @Override
                    public boolean onInstallAppAndAppOnForeground(String filePath) throws RemoteException {
                        //false 后台下载完提示安装,true下载完不进行操作,自行处理
                        return false;
                    }

                    @Override
                    public IBinder asBinder() {
                        return null;
                    }
                });
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    });
}

你可能感兴趣的:(android开辟新进程,远程服务的方式实现app升级)