继续
之前对比了用户和服务器端版本,当可升级的时候,要做进一步处理,而不仅仅是toast一个"有新版本"
case MSG_VERSION_DIFF: //弹出通知 Builder builder = new Builder(SplashActivity.this); builder.setTitle("发现新版本"); builder.setMessage(description); //需要实现一个MyDialogOnclickListener传进来 builder.setPositiveButton("升级",new MyDialogOnclickListener()); builder.setNegativeButton("取消",new MyDialogOnclickListener()); builder.show(); break; default: break; } }; }; class MyDialogOnclickListener implements OnClickListener{ @Override public void onClick(DialogInterface dialog, int which) { // TODO Auto-generated method stub } }
继续~对于点击事件,需要判断,确认就直接下载,取消就回去
确认:下载,这里用之前做的app,去info里改下信息,并将apk放到root里
涉及到文件的保存,这里用AsyncHttpClient,需要loopj,拷贝过来后,base里有问题,去掉原有导包重新来
class MyDialogOnclickListener implements OnClickListener{ @Override public void onClick(DialogInterface dialog, int which) { // TODO Auto-generated method stub if(which == DialogInterface.BUTTON_POSITIVE){ //用户点击了确认下载。这里开始去下载最新版本 AsyncHttpClient asyncHttpClient = new AsyncHttpClient(); //需要一个handler asyncHttpClient.get(downloadurl, new MyDownloadHandler()); }else{ //用户点击了取消下载。这时候应该跳入到主界面 } } } class MyDownloadHandler extends AsyncHttpResponseHandler{ @Override public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { // TODO Auto-generated method stub } @Override public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { // TODO Auto-generated method stub } }
下载并安装:
@Override public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { // TODO Auto-generated method stub File file = new File("storage/sdcard/mobilesafe.apk"); try { FileOutputStream fos = new FileOutputStream(file); fos.write(responseBody); fos.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } installapk(file); } private void installapk(File file) { // TODO Auto-generated method stub Intent intent = new Intent("android.intent.action.VIEW"); intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); startActivity(intent); }
此时走起一个发现解析错误,需要写权限
OK~能够安装
但是这个还有一个方法需要添加,显示安装进度,需要一个view,在splash的layout里加入
<TextView android:id="@+id/tv_splash_downloadprogress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_alignParentBottom="true" android:textColor="#000000" />
oncreate里初始化
@Override public void onProgress(int bytesWritten, int totalSize) { // TODO Auto-generated method stub super.onProgress(bytesWritten, totalSize); tv_splash_downloadprogress.setText(bytesWritten+"/"+totalSize); }
bin目录下的apk没有签名,不能发布到市场上,小范围安装可以
关于签名,导出整个项目时,
没有签名,系统有一个测试签名,在windows下的preference的build里有一个路径
这个可以填到location里,但是还需要一个password,一般就是android
自己创建的可以和系统的类似,改一下最后的文件名就好,第二次填密码那要注意填有效期,必须能达到2033年
发布不同版本时,首选是包名必须相同,其次是签名一致,所以在升级时要用到之前的签名,当然manifest里的版本号也要改下~
继续,当可以升级,用户点击取消则应该跳到homeactivity,要完成这个activity和layout
else{ //用户点击了取消下载。这时候应该跳入到主界面 enterHome(); } } } private void enterHome() { Intent intent = new Intent(SplashActivity.this, HomeActivity.class); startActivity(intent); }
homeactivity
import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class HomeActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); TextView tv = new TextView(this); tv.setText("我是主界面"); setContentView(tv); } }
manifest里要声明下
注意handler里,版本相同时,进入home
当出现意外时,比如服务器挂了,这个时候应该有相应处理,这时不能在异常处理里用enterhome(),因为主线程不能操作UI,所以应该利用handler发消息
注意,由于这里涉及多种消息的发送,其实可以做一些优化,把msg的声明拿出来,添加一个finally,在里面发消息,而各种状况里只需说明是哪种消息即可
异常里的消息
并且提示错误代码,给出反馈
有一个细节,当初设了一个notitle,但是背景成了黑色,现在要调成白色并且依然notitle,那么可以在values里的style加一个notitle条目,再在manifest里改主题
取消后,带参数的跳转
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // TODO Auto-generated method stub super.onActivityResult(requestCode, resultCode, data); if (requestCode==100 && resultCode==RESULT_CANCELED) { enterHome(); } }
这里有两个取消,一个是直接不升级,进入home,另一个是点了升级,但是在安装的时候又选择不安张,此时也应该可以跳转到home
在安装里应该是一个带参数的跳转
要实现进入下载或者homeactivity后点back键返回九宫格,则需要销毁这个activity,这样就能回到栈底
注意IP地址改动,服务器端的info文件里的地址也要改动。。
在超时等待的时候,加一个小圆圈转动,提升体验~需要ProgressBar
<ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/tv_splash_version" android:layout_marginTop="5dp" android:layout_centerHorizontal="true"/> </RelativeLayout>
这一段的完整代码
import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import org.apache.http.Header; import org.json.JSONException; import org.json.JSONObject; import com.loopj.android.http.AsyncHttpClient; import com.loopj.android.http.AsyncHttpResponseHandler; import utils.WebUtils; import android.app.Activity; import android.app.AlertDialog.Builder; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.widget.TextView; import android.widget.Toast; public class SplashActivity extends Activity { protected static final int MSG_VERSION_SAME =0; protected static final int MSG_VERSION_DIFF =1; protected static final int MSG_ERROR_URLMALFORMED = 10; protected static final int MSG_ERROR_JSON = 20; protected static final int MSG_ERROR_IO = 30; private String description; private String downloadurl; private String version; private TextView tv_splash_downloadprogress; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); TextView tv_splash_version = (TextView) findViewById(R.id.tv_splash_version); //上面的声明要注意是空字符串,而不是null tv_splash_version.setText("版本: "+getVersion()); //解析下载进度 tv_splash_downloadprogress = (TextView) findViewById(R.id.tv_splash_downloadprogress); //获取服务器端版本号 Update(); } Handler myHandler = new Handler(){ public void handleMessage(android.os.Message msg){ switch(msg.what){ case MSG_VERSION_SAME: //进入主界面 enterHome(); break; case MSG_VERSION_DIFF: //弹出通知 Builder builder = new Builder(SplashActivity.this); builder.setTitle("发现新版本"); builder.setMessage(description); //需要实现一个MyDialogOnclickListener传进来 builder.setPositiveButton("升级",new MyDialogOnclickListener()); builder.setNegativeButton("取消",new MyDialogOnclickListener()); builder.show(); break; case MSG_ERROR_IO: Toast.makeText(getApplicationContext(), "错误代码"+MSG_ERROR_IO, 1).show(); enterHome(); break; case MSG_ERROR_JSON: Toast.makeText(getApplicationContext(), "错误代码"+MSG_ERROR_JSON, 1).show(); enterHome(); break; case MSG_ERROR_URLMALFORMED: Toast.makeText(getApplicationContext(), "错误代码"+MSG_ERROR_URLMALFORMED, 1).show(); enterHome(); break; default: break; } }; }; class MyDialogOnclickListener implements OnClickListener{ @Override public void onClick(DialogInterface dialog, int which) { // TODO Auto-generated method stub if(which == DialogInterface.BUTTON_POSITIVE){ //用户点击了确认下载。这里开始去下载最新版本 AsyncHttpClient asyncHttpClient = new AsyncHttpClient(); //需要一个handler asyncHttpClient.get(downloadurl, new MyDownloadHandler()); }else{ //用户点击了取消下载。这时候应该跳入到主界面 enterHome(); } } } private void enterHome() { Intent intent = new Intent(SplashActivity.this, HomeActivity.class); startActivity(intent); //要实现点back键返回开始的activity,则需要销毁这个activity,这样就能回到栈底 finish(); } //进入安装后用户又取消,这时候应该能直接进入home,这是带参数值的跳转 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // TODO Auto-generated method stub super.onActivityResult(requestCode, resultCode, data); if (requestCode==100 && resultCode==RESULT_CANCELED) { enterHome(); } } class MyDownloadHandler extends AsyncHttpResponseHandler{ @Override public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { // TODO Auto-generated method stub File file = new File("storage/sdcard/mobilesafe.apk"); try { FileOutputStream fos = new FileOutputStream(file); fos.write(responseBody); fos.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } installapk(file); } private void installapk(File file) { // TODO Auto-generated method stub Intent intent = new Intent("android.intent.action.VIEW"); intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); startActivityForResult(intent,100); } @Override public void onProgress(int bytesWritten, int totalSize) { // TODO Auto-generated method stub super.onProgress(bytesWritten, totalSize); tv_splash_downloadprogress.setText(bytesWritten+"/"+totalSize); } @Override public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { // TODO Auto-generated method stub } } private void Update() { // TODO Auto-generated method stub new Thread(new Runnable(){ @Override public void run() { String path = "http://192.168.3.100/version_info.json"; Message msg = myHandler.obtainMessage(); try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(5000); conn.setConnectTimeout(2000); //实际发布版本的时候,超时实际最好稍微长一些 conn.setRequestMethod("GET"); conn.connect(); if (conn.getResponseCode()==200) { InputStream is= conn.getInputStream(); String jsontext = WebUtils.gettextFromInputStream(is, null); JSONObject jsonObject = new JSONObject(jsontext); version = jsonObject.getString("version"); description = jsonObject.getString("description"); downloadurl = jsonObject.getString("downloadurl"); System.out .println("SplashActivity.Update().new Runnable() {...}.run()" +version +description +downloadurl); if(version.equals(getVersion())){ //版本一致则进入主页 msg.what = MSG_VERSION_SAME; }else{ //不一致则弹出dialog告诉用户,此时需要handler来处理 msg.what = MSG_VERSION_DIFF; } } } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); msg.what = MSG_ERROR_URLMALFORMED; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); msg.what = MSG_ERROR_IO; }catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); msg.what = MSG_ERROR_JSON; }finally{ myHandler.sendMessage(msg); } } }).start(); } private String getVersion() { //获取版本号 PackageManager pm = getPackageManager(); PackageInfo packageinfo; String version=""; try { //第一个参数应该为manifest里的包名而不是工程里的包名,后直接以方法获取~ packageinfo = pm.getPackageInfo(getPackageName(), 0); version = packageinfo.versionName; } catch (NameNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return version; } }