Android Review—3
零蚀
-
网络简述
- webView简单应用
//申请网络权限后 WebView webView = (WebView) findViewById(R.id.web); //获取设置浏览属性的类websetting WebSettings settings = webView.getSettings(); //支持javascript脚本 settings.getJavaScriptEnabled(); //页面跳转时还是在webView里面进行显示 webView.setWebViewClient(new WebViewClient()); webView.loadUrl("http://www.baidu.com");
- HttpURLConnection简单应用
//耗时操作,要在线程里操作 new Thread(new Runnable() { @Override public void run() { //网络请求 request(); } }).start(); /* * 请求网络 * */ private void request() { try { //网址(baidu返回是null,hahaha) URL url=new URL(address); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(1000); connection.setReadTimeout(1000); InputStream inputStream= connection.getInputStream(); //对输入流进行缓冲读取,(缓冲的本质,是由于各个硬件的运作效率不同的产物) reader = new BufferedReader(new InputStreamReader(inputStream)); Log.e(TAG, "onCreate: "+reader.readLine() ); String line; while ((line= reader.readLine())!=null){ Log.e(TAG, "onCreate: "+line ); } } catch (Exception e) { Log.e(TAG, "Exception: "+e.getMessage() ); }finally { if(reader!=null){ try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } if(connection!=null){ connection.disconnect(); } } } // psot提交信息 // connection.setRequestMethod("POST"); // DataOutputStream out= new DataOutputStream(connection.getOutputStream()); // out.writeBytes("username=aaa&password=123");
- OkHttp的简单使用
🔗OkHttp的github链接
关于协议,加密,授权,编码,结构,网络请求方法原理等细节见网络篇- XML数据格式如下:
<total> <item> <id>001</id> <name>Anny</name> <age>120</age> </item> </total>
- XML解析之pull解析
//XML解析 String xmlData="<data>假如这是一个xml数据</data>"; //pull解析 parseXMLWithPull(xmlData); /** * @param xmlData */ private void parseXMLWithPull(String xmlData) { try { XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParser xmlPullParser = factory.newPullParser(); xmlPullParser.setInput(new StringReader(xmlData)); int eventType = xmlPullParser.getEventType(); while(eventType!=xmlPullParser.END_DOCUMENT){ String nodeName=xmlPullParser.getName(); switch (eventType){ //开始解析节点 case XmlPullParser.START_TAG: //匹配开始的标签对 if("id".equals(nodeName)){ id = xmlPullParser.nextText(); } //..... break; //完成解析某个节点 case XmlPullParser.END_TAG: if("item".equals(nodeName)){ Log.e(TAG,"id="+id); //.....打印item标签里的所有属性值 } } //将"指针"移到下一位 eventType=xmlPullParser.next(); } } catch (XmlPullParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
- XML解析之sax解析
//解析类 public class ContentHandle extends DefaultHandler { public static final String TAG="TAG_CONTENTHANDLE"; private String nodeName; private StringBuilder id; @Override public void startDocument() throws SAXException { super.startDocument(); id=new StringBuilder(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { super.startElement(uri, localName, qName, attributes); //记录当前的节点名 nodeName=localName; } @Override public void characters(char[] ch, int start, int length) throws SAXException { super.characters(ch, start, length); //添加内容 if ("id".equals(nodeName)){ id.append(ch, start, length); }else{ //...... } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { super.endElement(uri, localName, qName); if ("item".equals(localName)){ Log.d(TAG, "endElement: id is"+id.toString().trim()); Log.d(TAG, "endElement: name is"); Log.d(TAG, "endElement: XXX is"); } //清空StringBuilder id.setLength(0); } @Override public void endDocument() throws SAXException { super.endDocument(); } } /** * 解析方法 * @param xmlData */ private void parseXMLWithSAX(String xmlData) { try { SAXParserFactory factory = SAXParserFactory.newInstance(); XMLReader xmlReader = factory.newSAXParser().getXMLReader(); ContentHandle contentHandle=new ContentHandle(); xmlReader.setContentHandler(contentHandle); xmlReader.parse(new InputSource(new StringReader(xmlData))); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
- json数据格式一般如下:
{"id":1,"version":2,"name":"Anny"}
- json解析用的一般是GSON开源库
🔗GSON的Github链接
关于协议,加密,授权,编码,结构,网络请求方法原理等细节见网络篇
-
服务
服务并不会自动开启线程,所有代码默认运行在主线程中。所以要自行开发子线程。
- 多线程
//Thread类 * @param target * the object whose {@code run} method is invoked when this thread * is started. If {@code null}, this classes {@code run} method does * nothing. */ public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } ............. /** * If this thread was constructed using a separate * <code>Runnable</code> run object, then that * <code>Runnable</code> object's <code>run</code> method is called; * otherwise, this method does nothing and returns. * <p> * Subclasses of <code>Thread</code> should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } } ............
可见Thread通过init来创建一个线程,然后调用target(Runnable)接口方式将run()方法的内容放入线程中。
拓展见Android 线程篇(新增)
子线程中不能更新UI需要所以需要转化到主线程中更新UI
//切换到主线程中 runOnUiThread(new Runnable() { @Override public void run() { } }); //源码 /** * Runs the specified action on the UI thread. If the current thread is the UI * thread, then the action is executed immediately. If the current thread is * not the UI thread, the action is posted to the event queue of the UI thread. * * @param action the action to run on the UI thread */ public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
runOnUiThread是Activity中的一个方法,判断若是在线程中,则通过handle将runnable的内容post到主线程中。
- 异步消息处理之handler
@SuppressLint("HandlerLeak") Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1: Toast.makeText(Main2Activity.this, "来了老弟", Toast.LENGTH_SHORT).show(); break; } } }; /** *线程中发送消息 */ new Thread(new Runnable() { @Override public void run() { Message message = new Message(); message.what=1; handler.sendMessage(message); } }).start(); //这里有个内存泄漏的提示,可以在destory销毁handler解决,或者自定义,然后若引用解决 //@Override //protected void onDestroy() { // super.onDestroy(); // handler.removeCallbacksAndMessages(null); //}
//这种方法也能切回主线程 handler.post(new Runnable() { @Override public void run() { Log.e(TAG, "run: "+Thread.currentThread().getName() ); } });
- 解析异步消息
- Message 是线程间传递的消息,它可以在内部携带少量的信息,用于在不同线程间交换数据。除what字段外,还可以使用arg1和arg2字段来携带一些整形的数据,使用obj也可以携带一个object对象。
- Handler 是处理者,用于发送和处理消息,发送一般是sendMessage()方法,而发送消息是经历了一系列的辗转处理后最终送到了handlerMessage()方法中。
- MessageQueue() 是消息队列的意思,它主要存放所有通过handler发送的消息,这部分消息会一直存在于消息队列中等待被处理,每个线程中有一个消息队列
- Looper是线程中MessageQueue的管理者,调用Looper的loop()方法,就会进入一个无限循环的当中,然后发现MessageQueue中存在一个消息就会将它取出,并传递到handlerMessage中。每个线程都有一个Looper对象。
- AsyncTask
AsyncTask是一个抽象类。所以要继承他。
/** * AsyncTask的三个范型参数 * Params 执行时所要传入的参数,可用于后台任务中使用 * Progress 进度单位 * Result 返回值类型 */ public class MyTask extends AsyncTask<Void,Integer,Boolean> { //执行前调用,用于初始化操作 @Override protected void onPreExecute() { super.onPreExecute(); } //子线程中运行,在这里处理耗时任务。 @Override protected Boolean doInBackground(Void... voids) { //耗时任务 //将进度发送给Update方法 publishProgress(100); //return 的结果返回给onPostExecute方法接收处理 return null; } //更新程度 @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); } //结果 @Override protected void onPostExecute(Boolean aBoolean) { super.onPostExecute(aBoolean); } } //调用 new MyTask().execute();
- 服务
首先注册,或者在new中创建一个service,其中export指是否供外界调用。
<service android:name=".util.MyService" android:enabled="true" android:exported="true"></service>
启动/停止
//启动一个服务 Intent intent=new Intent(this, MyService.class); startService(intent); //停止一个服务 stopService(intent);
Service给Activity的通信
//创建binder对象,用于service和activity的媒介 //添加name来进行传值c测试 public class MyBinder extends Binder { String name=""; public MyBinder() { } public MyBinder(String name){ this.name=name; } public void startDownLoad(){ //下载 Log.e("TAG", "startDownLoad: "+name); } public void getProgress(){ //获取进度 } }
//创建service类,将binder对象返回 public class MyService extends Service { public MyService() { } /** * 服务绑定的方法 * @param intent * @return Return the communication channel to the service */ MyBinder binder=new MyBinder("来了老弟"); @Override public IBinder onBind(Intent intent) { return binder; } //第一次创建会调用 @Override public void onCreate() { super.onCreate(); } //只要服务启动就会走这个方法 //如果多次点击startService(),这个方法每次都会被执行到 @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } }
ServiceConnection conn=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //在服务绑定的时候被调用 MyBinder binder = (MyBinder) service; binder.startDownLoad(); binder.getProgress(); } @Override public void onServiceDisconnected(ComponentName name) { //服务解绑的时候被调用 } }; //绑定服务,将conn中的binder和service的binder绑定 //第三个参数,是服务自动创建,oncreate方法执行onstartcommand方法不执行 Intent binder=new Intent(this,MyService.class); bindService(binder,conn,BIND_AUTO_CREATE); //结束后别忘了解绑 unbindService(conn);
*前台服务
服务的优先级问题,虽然服务一般会运行在后台,但是由于系统不会让应用一直骚扰用户,所以服务的优先级是比较低的,所以想让服务一直存在,就要用到保活手段,首先,能用💰摆平的都不是问题,就是开通白名单,其次也可用一个像素activity保活手段,但是有些手机无效。除此之外,以下是提升Service优先级的方法,将后台服务变为前台服务,但是前台服务,会弹出一个Notification来提醒用户,根据用户意愿来设置://MyService中onCreate()方法 //创建通道 NotificationManager manager= (NotificationManager) getSystemService(NOTIFICATION_SERVICE); if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.O){ NotificationChannel channel = new NotificationChannel( "channelID", "channelName", NotificationManager.IMPORTANCE_HIGH); manager.createNotificationChannel(channel); } //前台服务 Intent intent=new Intent(this, Main2Activity.class); PendingIntent pi=PendingIntent.getActivities(this,0,new Intent[]{intent},0); Notification notification = new NotificationCompat.Builder(this,"channelID") .setContentText("Notifacation") .build(); startForeground(1,notification);
- IntentService
应用场景:服务内的异步线程执行逻辑结束,服务也跟着stop,相当于在服务里面开了一个子线程,线程结束,服务结束,(也要注册的,苟富贵,莫相忘)
//继承IntentService public class MyIntentService extends IntentService { /** * Creates an IntentService. Invoked by your subclass's constructor. * * @param name Used to name the worker thread, important only for debugging. */ public MyIntentService(String name) { super(name); } @Override protected void onHandleIntent(Intent intent) { //耗时操作的方法 Log.e("TAG", "努力耗时操作" ); Log.e("TAG", Thread.currentThread().getName() ); } @Override public void onDestroy() { super.onDestroy(); Log.e("TAG", "onDestroy: 执行到MyIntentService了" ); } }
//启动服务 Intent intent=new Intent(this,MyIntentService.class); startService(intentService);
- 基于位置的服务
定位功能分为两种,一个是GPS,一个是网络定位。GPS定位是手机内硬件自带的定位功能,精准度很高,但是缺点是只能在室外进行定位,室内无法接收到卫星的信号,网络定位是根据手机附近三个基站进行测试,以此来计算出手机和每个基站的距离,再通过三角定位确定出一个大致的范围,以下会用第三方库进行定位,百度地图:
第一步:需要发布版和开发版的SHA1,它指打包程序时候用的申请API KEY的必须要填的一个字段,它指打包程序时候所用签名文件的SHA1指纹,可以通过Android studio进行查看(debug):
便会在logcat中打印SHA1信息信息,目前用的是debug.keystore文件所生成的指纹,但是若要正式的签名,打包生成key,注意创建key store path 以文件名结尾,生成对应文件…/xx.jsk。
然后获取SHA1keytool -list -v -keystore keystore;
第二步:填入开发SHA1和发布SHA1
第三部:下载SDK,(我是找了一会没有发现跳转,点图标过来的,厉害了)
后续的接入函数,初始化SDK,文档写的非常详细,抄抄就行。至于导入SDK后的最初呈现效果,就不贴图了(最不喜欢贴图,还要重新上传,省事)。
mapStatus 地图状态
compassEnable 是否开启指南针,默认开启
mapType 地图模式,默认为普通地图
rotateGesturesEnabled 是否允许地图旋转手势,默认允许
scrollGesturesEnabled 是否允许拖拽手势,默认允许
overlookingGesturesEnabled 是否允许俯视图手势,默认允许
zoomControlsEnabled 是否显示缩放按钮控件,默认允许
zoomControlsPosition 设置缩放控件位置
zoomGesturesEnabled 是否允许缩放手势,默认允许
scaleControlEnabled 是否显示比例尺控件,默认显示
scaleControlPosition 设置比例尺控件位置
logoPosition 设置Logo位置- 案例:
//比如切换地图模式 //mBaiduMap是地图控制器对象 mBaiduMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE);
百度地图的文档基础功能有几点要注意的:
首先,很容易定位到一个4.9E-324的值,这可能是你权限问题,因为,手机基本都是6.0以上的,所以要动态申请权限,不然就获取经纬度失败,返回默认值4.9E-324。需要动态添加权限:
public class PermissionUtil { public static void request(Activity activity, PermissionInterface listener){ List<String> per = new ArrayList(); if(ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_COARSE_LOCATION)!= PackageManager.PERMISSION_GRANTED){ per.add(Manifest.permission.ACCESS_COARSE_LOCATION); } if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){ per.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); } if(ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED){ per.add(Manifest.permission.ACCESS_FINE_LOCATION); } if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.READ_PHONE_STATE)!= PackageManager.PERMISSION_GRANTED){ per.add(Manifest.permission.READ_PHONE_STATE); } if(per.size()!=0){ String permission[]=per.toArray(new String[]{}); ActivityCompat.requestPermissions(activity,permission,1101); }else{ if(listener!=null){ listener.granted(); } } } }
其次一点是,百度文档上的定位,是会在你的坐标上,多一个点标示,并不会自己把地图移动到你的位置上方,很坑,因为文档上一眼看上去也没有定位到目标位置的方法,所以查了一下方法如下:
MyLocationData locData = new MyLocationData.Builder() .accuracy(location.getRadius()) // 此处设置开发者获取到的方向信息,顺时针0-360 .direction(location.getDirection()).latitude(location.getLatitude()) .longitude(location.getLongitude()).build(); mBaiduMap.setMyLocationData(locData); /** * *------------以上是文档中获得经纬度坐标的方法---------------------- * */ //将地图移到我所定位的地方 LatLng latLng=new LatLng(locData.latitude,locData.longitude); MapStatusUpdate update= MapStatusUpdateFactory.newLatLng(latLng); mBaiduMap.animateMapStatus(update);
笔记资料来自书籍《第一行代码》郭霖,无商业目的
🔗 前言
🔗 Android Review 列表
🔗 Android Review—1
🔗 Android Review—2
🔗 Android Review—4
🔗 Android Review—5