編輯:關於Android編程
demo的邏輯過程:
1.進入程序
2.檢查是否有版本更新,如果有則詢問用戶是否更新,否則維持原狀
3.檢測當前網絡狀態並且詢問用戶是否進行版本更新,如果是則進行更新,否則維持原狀
4.切換網絡,當當前網絡為wifi時,檢查版本更新,重復2、3.
結構:
CommonAsyncTask:執行網絡請求操作
ConnectionUrl:記錄要請求的IP地址
NetworkHelp:網絡輔助類
upDateAppUtil:更新版本類
MainActivity:UI及執行界面
客戶端:
MainActivity:
public class MainActivity extends Activity { //接收網絡請求返回回調 private ListenerImpl mListenerImpl; private ProgressDialog m_progressDlg; private static final String TAG = "MainActivity"; private Dialog dialogs; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); m_progressDlg = new ProgressDialog(this); m_progressDlg.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); // 設置ProgressDialog 的進度條是否不明確 false 就是不設置為不明確 m_progressDlg.setIndeterminate(false); Log.d(TAG,"ONCREATE"); //注冊廣播接收器 registerReceiver(); //綁定網絡數據回調接收器 initListener(); // //獲取服務器版本 // updateAppUtil.getServerVersion(this); } protected void onStart(){ super.onStart(); Log.d(TAG, "ONSTART"); } /** * 網絡數據回調 */ public void initListener() { mListenerImpl = null; mListenerImpl = ListenerImpl.getInstance(); mListenerImpl.setOnListener(new Listener() { @Override public該類主要工作是注冊了一個網絡狀態改變的廣播接收器,當網絡狀態改變的時候就會執行不同的操作,但是經過這個demo發現他並非改變時才會發送廣播,進入app後也會發送廣播:void receiveData(T data) { Log.d(TAG, data.toString()); dealAfterResponse((String) data); } }); } /** * 解析忘了數據 * @param s */ private void dealAfterResponse(String s) { try { JSONObject object; object = new JSONObject(s); if (object.getInt("Success")==200) { //版本需要更新操作 if (object.getInt("appVersion")!= updateAppUtil.getAppVersion(this)){ Log.d(TAG, "not same"); if (NetworkHelp.isWifi(this)){ if (dialogs==null) showDialog("有版本更新,是否更新版本"); } else { if (dialogs==null) showDialog("有版本更新,當前不在wifi狀態,是否更新版本"); } } //版本不需要更新操作 else{ Log.d(TAG, "same"); } } } catch (JSONException e) { e.printStackTrace(); } } /** * 接收網絡狀態廣播消息 */ public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { ConnectivityManager connectivityManager=(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo mobNetInfo=connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); NetworkInfo wifiNetInfo=connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); if (!mobNetInfo.isConnected() && !wifiNetInfo.isConnected()) { Toast.makeText(context, "網絡狀態不可用", Toast.LENGTH_SHORT).show(); }else { dialogs=null; //獲取服務器版本 Log.d(TAG,"MyReceiver"); updateAppUtil.getServerVersion(context); } } //如果無網絡連接activeInfo為null } /** * 提示框 * @param str */ public void showDialog(String str){ dialogs = new AlertDialog.Builder(this).setTitle("軟件更新").setMessage(str) // 設置內容 .setPositiveButton("更新",// 設置確定按鈕 new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { m_progressDlg.setTitle("正在下載"); m_progressDlg.setMessage("請稍候..."); updateAppUtil.downNewApp(ConnectionUrl.GET_SERVER_IP, m_progressDlg); updateAppUtil.getAllFiles(new File("/sdcard/newApp")); } }) .setNegativeButton("暫不更新", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { dialogs.dismiss(); } }).create();// 創建 // 顯示對話框 dialogs.show(); } /** * 注冊廣播接收器 */ private void registerReceiver(){ IntentFilter filter=new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); MyReceiver myReceiver=new MyReceiver(); this.registerReceiver(myReceiver, filter); } protected void onDestroy(){ super.onDestroy(); Log.d(TAG,"ONDESTORY"); } protected void onPause(){ super.onPause(); Log.d(TAG,"ONPAUSE"); if (isFinishing()){ Log.d(TAG,"ONONON"); } } }
if (!mobNetInfo.isConnected() && !wifiNetInfo.isConnected()) { Toast.makeText(context, "網絡狀態不可用", Toast.LENGTH_SHORT).show(); }else { dialogs=null; //獲取服務器版本 Log.d(TAG,"MyReceiver"); updateAppUtil.getServerVersion(context); }
updateAppUtil.getServerVersion(context);
該類還有一個工作是注冊了一個回調,接收服務器返回的版本號並且調用dealAfterResponse方法解析:public void initListener() { mListenerImpl = null; mListenerImpl = ListenerImpl.getInstance(); mListenerImpl.setOnListener(new Listener() { @Override public調用getAppVersion能夠獲取當前app的版本號,版本號不同就會詢問是否更新,判斷不同的網絡狀態,彈出不同內容的提示框——showDialog():void receiveData(T data) { Log.d(TAG, data.toString()); dealAfterResponse((String) data); } }); }
if (object.getInt("appVersion")!= updateAppUtil.getAppVersion(this)){ Log.d(TAG, "not same"); if (NetworkHelp.isWifi(this)){ if (dialogs==null) showDialog("有版本更新,是否更新版本"); } else { if (dialogs==null) showDialog("有版本更新,當前不在wifi狀態,是否更新版本"); } } //版本不需要更新操作 else{ Log.d(TAG, "same"); }
點確定後調用以下方法下載並且安裝新版本app:
updateAppUtil.downNewApp(ConnectionUrl.GET_SERVER_IP, m_progressDlg);
updateAppUtil
該類封裝了一些更新app版本要用到的一些方法。
public class updateAppUtil { private static Context mContext; private static ProgressDialog progressDialog; private static final String DIRECTORY_NAME = "/newApp"; private static final String File_NAME = "NewVersion.apk"; private static final String TAG = "updateAppUtil"; /** * 獲取本app版本號 * @param context * @return */ public static int getAppVersion(Context context) { mContext =context; int verCode = -1; try { //對應AndroidManifest.xml裡的package部分 verCode = context.getPackageManager().getPackageInfo( "com.test.tangjiarao.versionupdate", 0).versionCode; } catch (PackageManager.NameNotFoundException e) { Log.e("msg", e.getMessage()); } return verCode; } /** * 獲取服務器的版本號 * @param context */ public static void getServerVersion(Context context){ Log.d(TAG,"getServerVersion"); new CommonAsyncTask(context).execute("get", ConnectionUrl.GET_SERVER_IP); } /** * 創建文件路徑 */ public static File getDirectory(){ File file = new File(Environment.getExternalStorageDirectory() + DIRECTORY_NAME); //如果該路徑不存在,則創建文件夾 if (!file.exists()) { file.mkdir(); } return file; } /** * 獲取目標路徑下的文件 * @param root */ public static void getAllFiles(File root){ File files[] = root.listFiles(); if(files != null) for(File f:files){ if(f.isDirectory()){ getAllFiles(f); } else{ Log.d(TAG, f.getName()); } } } /** * 下載app * @param path * @param mProgressDialog */ public static void downNewApp(String path,ProgressDialog mProgressDialog) { progressDialog =mProgressDialog; progressDialog.show(); new Thread() { public void run() { URL url = null; FileOutputStream fos = null; BufferedInputStream bis = null; HttpURLConnection connection = null; try { url = new URL(ConnectionUrl.DOWN_NEW_APP); connection = (HttpURLConnection) url.openConnection(); //不能獲取服務器響應 if (HttpURLConnection.HTTP_OK != connection.getResponseCode()) { Message message = Message.obtain(); message.what = 1; handler.sendMessage(message); } //不存在sd卡 else if (Environment.getExternalStorageState() .equals(Environment.MEDIA_UNMOUNTED)){ Message message=Message.obtain(); message.what=2; handler.sendMessage(message); } //滿足上兩個條件 else{ //獲取網絡輸入流 bis = new BufferedInputStream(connection.getInputStream()); //文件大小 int length = connection.getContentLength(); progressDialog.setMax((int)length); //緩沖區大小 byte[] buf = new byte[10]; int size =0; //獲取存儲文件的路徑,在該路徑下新建一個文件為寫入流作准備 File cfile = new File(getDirectory().getPath(), File_NAME); //如果不存在則新建文件 if (!cfile.exists()) { cfile.createNewFile(); } //將流與文件綁定 fos = new FileOutputStream(cfile); //記錄進度條 int count=0; //保存文件 while ((size = bis.read(buf)) != -1) { fos.write(buf, 0, size); count += size; if (length > 0) { progressDialog.setProgress(count); } } Log.d("JSON",count+""); Log.d("JSON","HAHA"+cfile.getAbsolutePath()+cfile.getName()); Bundle bundle=new Bundle(); Message message=Message.obtain(); message.what=3; bundle.putString("msg", cfile.getAbsolutePath()); message.setData(bundle); handler.sendMessage(message); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { try { if (fos!= null) { fos.close(); } if (bis != null) { bis.close(); } if (connection!= null) { connection.disconnect(); } } catch (Exception e) { e.printStackTrace(); } } } }.start(); } private static Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case 1: Toast.makeText(mContext, "網絡狀態不可用", Toast.LENGTH_SHORT).show(); Log.d(TAG, "網絡不通"); break; case 2: Toast.makeText(mContext, "請插入SD卡", Toast.LENGTH_SHORT).show(); Log.d(TAG, "沒有sd卡"); break; case 3: Bundle bundle = msg.getData(); String fileName = bundle.getString("msg"); installAPK(fileName,mContext); Log.d(TAG, "已經下載"); break; default: break; } }; }; /** * 安裝app * @param fileName * @param mContext */ private static void installAPK(String fileName,Context mContext){ File file =new File(fileName); if(!file.exists()){ return; } Intent intent=new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(Intent.ACTION_VIEW); Log.d(TAG,"AA"+"file://"+file.toString()); //"file://"+file.toString()下載的app的路徑 intent.setDataAndType(Uri.parse("file://"+file.toString()), "application/vnd.android.package-archive"); mContext.startActivity(intent); } }CommonAsyncTask
public class CommonAsyncTask extends AsyncTask{ //顯示UI的組件 private Context mContext; //回調 private ListenerImpl listener; //調用標識 private String flag; //訪問url private String url; private String httpFuntion; //post傳參 private Map parameters; private final String TAG="CommonAsyncTask"; public CommonAsyncTask(Context mContext){ this.mContext = mContext; } //onPreExecute方法用於在執行後台任務前做一些操作 protected void onPreExecute() { super.onPreExecute(); Log.i(TAG, "onPreExecute() called"); if (!(NetworkHelp.isConnected(mContext))) { Toast.makeText(mContext, "網絡狀態不可用", Toast.LENGTH_SHORT).show(); return; } } //doInBackground方法內部執行後台任務,不可在此方法內修改UI @Override protected String doInBackground(String... params) { //get方法或者post方法的標識 httpFuntion= params[0]; url = params[1]; if(httpFuntion.equals("post")){ // flag =params[2]; // parameters = new HashMap<>(); // switch (flag) { // case "text" : // // parameters.put("account", params[3]); // break; // } return NetworkHelp.sendDataByPost(parameters, "utf-8", url); } else{ return NetworkHelp.getDataByGet("utf-8", url); } } //onProgressUpdate方法用於更新進度信息 @Override protected void onProgressUpdate(Integer... progresses) { Log.i(TAG, "onProgressUpdate(Progress... progresses) called"); } //onPostExecute方法用於在執行完後台任務後更新UI,顯示結果 @Override protected void onPostExecute(String result) { Log.i(TAG, "onPostExecute(Result result) called"); super.onPostExecute(result); //獲取返回數據後給MainActivity listener = null; listener = ListenerImpl.getInstance(); listener.transferData(result); clear(); } @Override protected void onCancelled() { Log.i(TAG, "onCancelled() called"); } protected void clear(){ parameters = null; flag = null; url = null; httpFuntion = null; } }
NetWorkHelp
public class NetworkHelp { private static final String TAG ="NetworkHelp"; private static final int TIMEOUT_MILLIONS = 8000; /** * 判斷網絡是否連接 * * @param context * @return * */ public static boolean isConnected(Context context) { ConnectivityManager connectivity = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); if (null != connectivity) { NetworkInfo info = connectivity.getActiveNetworkInfo(); if (null != info && info.isConnected()) { if (info.getState() == NetworkInfo.State.CONNECTED) { return true; } } } return false; } /** * 判斷是否是wifi連接 */ public static boolean isWifi(Context context) { ConnectivityManager connectivity = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); if (connectivity == null) return false; return connectivity.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI; } /** * Get funtion * @param encode * @param path * @return */ public static String getDataByGet(String encode, String path){ URL url =null; HttpURLConnection connection =null; InputStream inptStream =null; int responseCode; try { url = new URL(path); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setReadTimeout(TIMEOUT_MILLIONS); connection.setConnectTimeout(TIMEOUT_MILLIONS); connection.setDoInput(true); connection.setUseCaches(false); responseCode = connection.getResponseCode(); if(responseCode == HttpURLConnection.HTTP_OK) { inptStream = connection.getInputStream(); Log.d(TAG,"GET FUNCTION OK"); return dealResponseResult(inptStream,encode); } } catch (IOException e) { return "err: " + e.getMessage().toString(); } finally { try { if (connection != null) { connection.disconnect(); } if (inptStream != null) { inptStream.close(); } } catch (IOException e) { e.printStackTrace(); } } return ""; } public static String sendDataByPost(MapConnectionUrlparams, String encode, String path) { URL url=null; HttpURLConnection connection = null; OutputStream outputStream = null; InputStream inputStream = null; int responseCode; byte [] data = getRequestData(params, encode).toString().getBytes(); try { url = new URL(path); connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("POST"); connection.setConnectTimeout(TIMEOUT_MILLIONS); connection.setReadTimeout(TIMEOUT_MILLIONS); connection.setDoInput(true); connection.setDoOutput(true); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.setRequestProperty("Content-Length", String.valueOf(data.length)); outputStream = connection.getOutputStream(); outputStream.write(data, 0, data.length); responseCode = connection.getResponseCode(); if (responseCode == 200) { Log.d(TAG,"POST FUNCTION OK"); inputStream = connection.getInputStream(); return dealResponseResult(inputStream, encode); } } catch (Exception e) { } finally { try { if (outputStream != null) { outputStream.close(); } if (inputStream != null) { inputStream.close(); } if (connection != null) { connection.disconnect(); } } catch (Exception e) { e.printStackTrace(); } } return ""; } public static StringBuffer getRequestData(Map params, String encode) { StringBuffer buffer = new StringBuffer(); try { for (Map.Entry entry : params.entrySet()) { buffer.append(entry.getKey()) .append("=") .append(URLEncoder.encode(entry.getValue(), encode)) .append("&"); } buffer.deleteCharAt(buffer.length() - 1); } catch (Exception e) { e.printStackTrace(); } return buffer; } public static String dealResponseResult(InputStream inputStream, String encode) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte [] data = new byte[1024]; int lenngth = 0; try { while ((lenngth = inputStream.read(data)) != -1) { byteArrayOutputStream.write(data, 0, lenngth); } return new String(byteArrayOutputStream.toByteArray(), encode); } catch (Exception e) { e.printStackTrace(); } return ""; } }
public class ConnectionUrl { //獲取版本號IP public static String GET_SERVER_IP = "http://192.168.0.62:3000/getVersion"; //下載app IP public static String DOWN_NEW_APP = "http://192.168.0.62:3000/updateApp"; }
var express = require('express'); var router = express.Router(); /* GET home page. */ router.get('/updateApp', function(req, res, next) { ///Users/tangjiarao/version2.apk是該版本2apk在你計算機中的路徑 res.download("/Users/tangjiarao/version2.apk","version2"); }); router.get('/getVersion', function(req, res, next) { //返回版本號 res.json({"Success":200,"appVersion":2}); }); module.exports = router;演示:
進入程序界面&不更新&更新
安裝&更新完成
問題:進入版本1app調用一次getServerVersion()調用一次,而更新版本2後,進入app捕抓不了mainActivy生命周期動作,並且調用兩次getServerVersion()方法。
進入版本1app:下載版本2後: 猜測:是否是因為廣播接收器沒有注銷?
本Demo的實現效果是調用手機上已安裝的照相機來實現拍照的功能,拍好的照片以ImageView形式展示。 目的:學習手機調用安裝的相機照相,對大的圖片處理有所認識,這裡主
Android中的傳遞有兩個方法,一個是Serializable,另一個是Parcelable。 Serializable是J2SE本身就支持的。而Parc
React Native出自Facebook之手, 而且剛剛更新了文檔, 差一點我就放棄它了, 然而又撈了回來, 相比其他, 畢竟還是大公司大品牌有保障. 不多說了, 想
1、View和ViewRootViewRoot從名稱上來理解似乎是“View樹的根”,這很容易讓人產生誤解。因為ViewRoot並不屬於View樹