首先我得感谢我的大师兄以及我的师姐,还有网上的一个朋友,没有他们的帮忙我也做不出来。


进入正题吧,首先在android2.3.3/development里面新建一个文件夹(比如说test),然后在test里面建两个文件,Android.mk与screencap.cpp(截屏的一些基础知识请看我的博客 浅谈android截屏http://my.oschina.net/JumpLong/blog/75556

)

/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */#include <utils/Log.h>#include <errno.h>#include <netdb.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <binder/IPCThreadState.h>#include <binder/ProcessState.h>#include <binder/IServiceManager.h>#include <binder/IMemory.h>#include <surfaceflinger/ISurfaceComposer.h>#include <SkImageEncoder.h>#include <SkBitmap.h>#include <SkStream.h>using namespace android;long getCurrentTime(){struct timeval tv;gettimeofday(&tv,NULL);return tv.tv_sec * 1000 + tv.tv_usec / 1000 ;}long SocketPre,SocketAft,SocketTime;long CapPre,CapAft,CapTime;long SendPre,SendAft,SendTime;long EncodePre,EncodeAft,EncodeTime;int SetSockfd(char* strip,char* strport,int* psockfd){SocketPre = getCurrentTime();struct sockaddr_in server_addr;in_addr_t inaddrip;        int portnumber;if((inaddrip=inet_addr(strip))==INADDR_NONE)        {                fprintf(stderr,"ip Error!\n");LOGD("ip error!");                return -1;        }        if((portnumber=atoi(strport))<0)        {                fprintf(stderr,"portnumber Error\n");LOGD("port error!");                return -1;        }/* 客户程序开始建立 sockfd描述符  */        if((*psockfd=socket(AF_INET,SOCK_STREAM,0))==-1)        {                fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));LOGD("socketcreate error!");                return -1;        }        /* 客户程序填充服务端的资料       */        bzero(&server_addr,sizeof(server_addr));        server_addr.sin_family=AF_INET;        server_addr.sin_port=htons(portnumber);server_addr.sin_addr.s_addr=inaddrip;        /* 客户程序发起连接请求         */        if(connect(*psockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)        {                fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));LOGD("connect error!");                return -1;        }SocketAft = getCurrentTime();SocketTime = SocketAft - SocketPre;return 0; }char* CapScreen(int* len){const String16 name("SurfaceFlinger");sp<ISurfaceComposer> composer;getService(name, &composer);sp<IMemoryHeap> heap;uint32_t w, h;PixelFormat f;EncodePre = getCurrentTime();status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);if (err != NO_ERROR) {fprintf(stderr, "screen capture failed: %s\n", strerror(-err));return NULL;}EncodeAft = getCurrentTime();EncodeTime = EncodeAft - EncodePre;printf("screen capture success: w=%u, h=%u, pixels=%p,heapsize = %zu,heapsize = %zx\n",w, h, heap->getBase() ,heap->getSize(),heap->getSize());SkBitmap b;b.setConfig(SkBitmap::kARGB_8888_Config, w, h);b.setPixels(heap->getBase());printf("size = %ld\n",sizeof(b));CapPre = getCurrentTime();SkDynamicMemoryWStream stream;SkImageEncoder::EncodeStream(&stream, b,SkImageEncoder::kJPEG_Type,SkImageEncoder::kDefaultQuality);*len=stream.getOffset();        char* buffer=new char[*len];memcpy(buffer,stream.getStream(),*len);CapAft = getCurrentTime();CapTime = CapAft - CapPre;return buffer;}int SendScreen(int sockfd){SendPre = getCurrentTime();        int jpglong;char* jpgbuffer=CapScreen(&jpglong);if(jpgbuffer==NULL){printf("CapScreen failed!\n");return -1;}//加jpg头和长度printf("screen size  is %d byte\n",jpglong);char* pbuffer=new char[jpglong+6];pbuffer[0]='\r';pbuffer[1]='\n';memcpy(pbuffer+2,&jpglong,sizeof(int));memcpy(pbuffer+6,jpgbuffer,jpglong);delete jpgbuffer;if(send(sockfd,pbuffer,jpglong+6,0)<0)  {  LOGD("send error");printf("send error closed!\n");delete pbuffer;return -1; } SendAft = getCurrentTime();SendTime = SendAft - SendPre;delete pbuffer;return 0;}int main(int argc, char** argv){if(argc != 3){                fprintf(stderr,"Usage:%s ip portnumber\a\n",argv[0]);                exit(1);}int sockfd;if(SetSockfd(argv[1],argv[2],&sockfd)==-1){fprintf(stderr,"setsockfd failed!\n");exit(1);}        while(true){long pre = getCurrentTime();if(SendScreen(sockfd)!=0){printf("SendScreen failed!\n");close(sockfd);       return -1;}long aft = getCurrentTime();long useTime = aft - pre;printf("total %ld,setsocket %ld,cap %ld,send %ld,encode %ld\n",useTime,SocketTime,CapTime,SendTime,EncodeTime);}    close(sockfd);return 0;}

这里面主要是调用了screencap那个文件夹里面的screencap.cpp里面的截屏方法,然后加上了socket传输,getCurrentTime只是我用来测试截屏,传输,解码的所用时间的。这个程序运行的话需要三个参数,第一个参数就是执行嘛,第二个就是服务器的IP,第三个就是服务器的端口号。等下再说。。。。

大家都知道要想图片流畅,那么每秒显示的图片数量得有24幅,但是这个目前只能达到5幅,因为它截屏大概是60ms,但是编码得需要大概120ms,也就是平均得要0.2s,如果做成多线程的话,也就是说利用生产者与消费者的模式,生产者负责capture,消费者负责encode,那样的话至少每秒会产生10幅图片(尚未解决,求高手指点)


Android.mk

LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_SRC_FILES:= \screencap.cppLOCAL_SHARED_LIBRARIES := \libcutils \libutils \libbinder \libskia \    libui \    libsurfaceflinger_clientLOCAL_MODULE:= whilecapLOCAL_MODULE_TAGS := testsLOCAL_C_INCLUDES += \external/skia/include/core \external/skia/include/effects \external/skia/include/images \external/skia/src/ports \external/skia/include/utilsinclude $(BUILD_EXECUTABLE)
有了这两个文件后,进入终端,转到test目录,然后mm,没错的话就会出现

Install: out/target/product/generic/system/bin/whilecap

然后将这个whilecap push到已经root的手机里面,注意不要随便放在一个文件里面,sdcard就不行,可以放在system里面,但是在push到system的时候会提示read-only system,这个时候就需要一下几步:adb shell ->su ->mount -o rw,remount -t ext3 /dev/block/mmcblk1p21 /system,然后再次adb push whilecap /system (当然这是你的whilecap已经被你移到home文件夹的情况下)。

现在假设服务器程序已经安装了,那么你怎么去执行呢?adb shell -> /system/whilecap 192.168.0.*** **** 这就是前面说的三个参数,第二个是IP,第三个是port。

当然我们想的不是每次都要去用命令行去执行,多麻烦,于是有了下一步的使用java去调用命令行语句。


   public static int RunRootCommand(String paramString)    {      int i = 0;      Process localProcess = null;      try      {        localProcess = Runtime.getRuntime().exec("su");        OutputStreamWriter localOutputStreamWriter = new OutputStreamWriter(localProcess.getOutputStream(), "UTF-8");       // localOutputStreamWriter.write("export LD_LIBRARY_PATH=endorb:/systemb\n");        //localOutputStreamWriter.flush();        localOutputStreamWriter.write(paramString + "\n");        localOutputStreamWriter.flush();        localOutputStreamWriter.write("exit\n");        localOutputStreamWriter.flush();        localOutputStreamWriter.close();        localProcess.waitFor();        int j = localProcess.exitValue();        if (j == 0)          i = 1;        try        {          localProcess.destroy();          return i;        }        catch (Exception localException4)        {        System.out.println(localException4.getMessage());          while (true)            localException4.printStackTrace();        }      }      catch (Exception localException2)      {      System.out.println(localException2.getMessage());        while (true)        {          localException2.printStackTrace();          try          {            localProcess.destroy();          }          catch (Exception localException3)          {            localException3.printStackTrace();          }        }      }      finally      {      }    }

新建一个android程序,在mainActivity中调用此函数,传送的字符串参数就是我们刚才使用的三个参数合在一起的结果,比如

            RunRootCommand("/system/whilecap  192.168.0.100  7787");


这样,客户端的程序就完成了。

下面是服务器端的程序,分为两个,一个是android版本的,一个是java project。

android版本:

package com.example.jumplong;import java.io.DataInputStream;import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;import java.net.SocketException;import java.util.ArrayList;import java.util.Date;import java.util.concurrent.atomic.AtomicInteger;import android.app.Activity;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.Menu;import android.view.MenuInflater;import android.view.MenuItem;import android.view.Window;import android.view.WindowManager;import android.widget.ImageView;import android.widget.Toast;public class TCA extends Activity {/** Called when the activity is first created. */Boolean state = false;Bitmap bitmap = null;ImageView iv = null;Thread severThread;static Handler mHandler;long pre,aft;int photo;static ServerSocket ss = null;static Socket cs = null;static InputStream in = null;static DataInputStream din = null;static CriticQueue cq;static imgReceiver ir;static imgShower is;static Thread tir;static Thread tis;static AtomicInteger imgCount;@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// TODO Auto-generated method stubMenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.menu, menu);return true;}@Overridepublic boolean onMenuItemSelected(int featureId, MenuItem item) {switch (item.getItemId()) {case R.id.start:Log.e("tc"," current Time ��"+new Date( System.currentTimeMillis()));start();return true;case R.id.stop:stop();return true;default:return super.onOptionsItemSelected(item);}}private void start() {Log.e("tc", "enter the TCLCA start()");state = true;iv = (ImageView) findViewById(R.id.showPhoto);//bitmapseverThread = new Thread(new ServeThread()," ServeThread() ");severThread.start();}private void stop() {// TODO Auto-generated method stubstate = false;try {ss.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}aft = System.currentTimeMillis();Toast.makeText(getApplicationContext(), "photo " + photo + " time " + ( aft - pre ), Toast.LENGTH_SHORT).show();// android.os.Process.killProcess((int)Thread.getId());finish();// ActivityManager manager =// (ActivityManager)getSystemService(ACTIVITY_SERVICE);// manager.killBackgroundProcesses(getPackageName());}@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,      WindowManager.LayoutParams.FLAG_FULLSCREEN);   // 去除标题栏  requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.main);}class ServeThread implements Runnable {public ServeThread() {mHandler = new Handler() {public void handleMessage(Message msg) {switch (msg.what) {case 1:iv.setImageBitmap(bitmap); // ��ImageView����ʾBitmapLog.e("tc", "setImageBitmap sucess");break;default:break;}super.handleMessage(msg);}};}@Overridepublic void run() {try {ss = new ServerSocket(12345);} catch (IOException e) {Log.e("tc", "get the ServerSocket(54321) failed ");}imgCount = new AtomicInteger();// 0while (true) {try {cs = ss.accept();pre = System.currentTimeMillis();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}try {in = cs.getInputStream();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}din = new DataInputStream(in);cq = new CriticQueue();ir = new imgReceiver(cq);is = new imgShower(cq);tir = new Thread(ir, "thread-p");tis = new Thread(is, "thread-c");tir.start();tis.start();}}//run end}// class ServeThread endclass imgInfo {private byte[] imglocation;public void setimglocation(byte[] imgloc) {this.imglocation = imgloc;}}class CriticQueue {ArrayList<imgInfo> imgInfos = new ArrayList<imgInfo>();public void produce(imgInfo imginfo) {if (imgInfos.add(imginfo)){imgCount.incrementAndGet();}}// produce endpublic synchronized void consume() throws IOException {while (imgInfos.size() == 0) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}imgInfo img = imgInfos.get(0);imgInfos.remove(0);// first stagebitmap = BitmapFactory.decodeByteArray(img.imglocation, 0,img.imglocation.length);photo++;Message amessage = new Message();amessage.what = 1;mHandler.sendMessage(amessage);img = null;imgCount.decrementAndGet();}}class imgReceiver extends Thread {private CriticQueue imgReceiver_crique;boolean streamIsEnd = false;imgReceiver(CriticQueue q) {this.imgReceiver_crique = q;}public void run() {try {// search for mark of the imageint s0 = 0;int s1 = 0;int imgsize = 0;int inter = 0;byte[] len = new byte[4];while ( !streamIsEnd  ) {// search for 13s0 = din.read();while (s0 == -1) {s0 = din.read();}while (s0 != 13) {s0 = din.read();}if (din.read() == 10) {try {s1 = din.read();while ((s1 != -1) && (inter < 4)) {len[inter] = (byte) s1;inter++;s1 = din.read();}if (inter == 4) {imgsize = (len[0] & 0xff)| ((len[1] << 8) & 0xff00)| ((len[2] << 24) >>> 8)| (len[3] << 24);inter=0;}else{streamIsEnd = true;}if (!streamIsEnd) {byte[] img = new byte[imgsize];int readonce = 0;int totalread = 0;img[0] = (byte) s1;totalread++;while (totalread != imgsize) {readonce = din.read(img, totalread, imgsize- totalread);if (readonce == -1) {streamIsEnd = true;break;}totalread += readonce;if (totalread == imgsize) {break;}}if (!streamIsEnd) {imgInfo message = new imgInfo();message.setimglocation(img);imgReceiver_crique.produce(message);} else {din.close();in.close();cs.close();while (imgCount.get() > 0) {}streamIsEnd = true;}} else {din.close();in.close();cs.close();while (imgCount.get() > 0) {}streamIsEnd = true;}} catch (IOException e) {e.printStackTrace();}}// if 10 end}// while( !streamIsEnd ) end} catch (SocketException e) {try {din.close();in.close();cs.close();while (imgCount.get() > 0) {}} catch (IOException e1) {e1.printStackTrace();}} catch (IOException e) {e.printStackTrace();}}// run end}// imgReceiver endclass imgShower extends Thread {private CriticQueue imgShower_crique = null;imgShower(CriticQueue q) {this.imgShower_crique = q;}public void run() {while (true) {if (state == true) {while (imgCount.get() < 1) {}try {imgShower_crique.consume();} catch (IOException e) {e.printStackTrace();}}}}}// imgShower end}// class TCA end


main.XML

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:orientation="vertical" >        <ImageView        android:id="@+id/showPhoto"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        />    </LinearLayout>

最后添加权限

            <uses-permission android:name ="android.permission.ACCESS_WIFI_STATE"/>            <uses-permission android:name ="android.permission.CHANGE_WIFI_STATE"/>            <uses-permission android:name ="android.permission.CHANGE_NETWORK_STATE"/>            <uses-permission android:name ="android.permission.ACCESS_NETWORK_STATE"/>            <uses-permission android:name="android.permission.INTERNET" />

然后java project版本:

package com.jump.screencapclient;import java.awt.BorderLayout;import java.awt.Container;import java.awt.Graphics;import java.awt.Image;import java.awt.Toolkit;import java.awt.image.BufferedImage;import java.io.ByteArrayInputStream;import java.io.DataInputStream;import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;import java.net.SocketException;import java.util.ArrayList;import java.util.concurrent.atomic.AtomicInteger;import javax.imageio.ImageIO;import javax.swing.ImageIcon;import javax.swing.JFrame;import javax.swing.JLabel;public class test{static JFrame frame = new JFrame();final static JFrame win = new JFrame("JumpLong"); ;static Graphics g;Image img;static long pre ;long aft;static int photo;static Boolean state = false;static ServeThread severThread;static ServerSocket ss = null;static Socket cs = null;static InputStream in = null;static DataInputStream din = null;static CriticQueue cq;static imgReceiver ir;static imgShower is;static Thread tir;static Thread tis;static AtomicInteger imgCount;static BufferedImage image;/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubContainer contentPane;win.setSize(480,900);win.setVisible(true);win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//關閉g = win.getGraphics();severThread = new ServeThread("tcl capture");Thread t = new Thread(severThread);t.start();}public static class ServeThread implements Runnable {        String name;public ServeThread(String name){            this.name =  name;    }@Overridepublic void run() {System.out.println("run");try {ss = new ServerSocket(12345);System.out.println("open the socket   ");} catch (IOException e) {System.out.println("get the ServerSocket(54321) failed ");}imgCount = new AtomicInteger();// 0while (true) {try {cs = ss.accept();pre = System.currentTimeMillis();System.out.println("accept   ");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}try {in = cs.getInputStream();System.out.println("get the inputstream   ");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}din = new DataInputStream(in);System.out.println("after din   ");cq = new CriticQueue();System.out.println("after cq");ir = new imgReceiver(cq);System.out.println("after ir   ");is = new imgShower(cq); System.out.println("after is   ");tir = new Thread(ir, "thread-p");System.out.println("after tir   ");tis = new Thread(is, "thread-c");System.out.println("after tis   ");tir.start();System.out.println("after tir   ");tis.start();System.out.println("after tis   ");}}//run end}// class ServeThread endpublic static class imgInfo {private byte[] imglocation;public void setimglocation(byte[] imgloc) {this.imglocation = imgloc;}}public static class CriticQueue {ArrayList<imgInfo> imgInfos = new ArrayList<imgInfo>();public void produce(imgInfo imginfo) {if (imgInfos.add(imginfo)){imgCount.incrementAndGet();}}// produce endpublic synchronized void consume() throws IOException {System.out.println("consume");while (imgInfos.size() == 0) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}imgInfo img = imgInfos.get(0);imgInfos.remove(0);System.out.print("remove  ");// first stageByteArrayInputStream in = new ByteArrayInputStream(img.imglocation);    //将b作为输入流;image = ImageIO.read(in);     //将in作为输入流,读取图片存入image中,而这里in可以为ByteArrayInputStream();System.out.print("image  " + photo++ +"time " + ( System.currentTimeMillis()-pre ));g.drawImage(image,0,0, win);//JFrame frame = new JFrame();//frame.invalidate();//JLabel label = new JLabel(new ImageIcon(image));//System.out.print("show the image  ");//frame.getContentPane().add(label, BorderLayout.CENTER);//frame.pack();//frame.setVisible(true); ////�رմ���--�˳�����//frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);img = null;imgCount.decrementAndGet();}}public static class imgReceiver extends Thread {private CriticQueue imgReceiver_crique;boolean streamIsEnd = false;imgReceiver(CriticQueue q) {this.imgReceiver_crique = q;}public void run() {try {// search for mark of the imageint s0 = 0;int s1 = 0;int imgsize = 0;int inter = 0;byte[] len = new byte[4];while ( !streamIsEnd  ) {// search for 13s0 = din.read();System.out.println("din.read()  ");while (s0 == -1) {s0 = din.read();System.out.println("s0 == -1");}while (s0 != 13) {s0 = din.read();System.out.println("s0 != 13  ");}if (din.read() == 10) {try {System.out.println("din.read() == 10");s1 = din.read();while ((s1 != -1) && (inter < 4)) {len[inter] = (byte) s1;inter++;s1 = din.read();}if (inter == 4) {imgsize = (len[0] & 0xff)| ((len[1] << 8) & 0xff00)| ((len[2] << 24) >>> 8)| (len[3] << 24);inter=0;}else{streamIsEnd = true;System.out.println("streamisend = true  ");}if (!streamIsEnd) {System.out.println("!streamisend");byte[] img = new byte[imgsize];int readonce = 0;int totalread = 0;img[0] = (byte) s1;totalread++;System.out.println("titalread = " + totalread);while (totalread != imgsize) {readonce = din.read(img, totalread, imgsize- totalread);if (readonce == -1) {streamIsEnd = true;break;}totalread += readonce;if (totalread == imgsize) {break;}}if (!streamIsEnd) {imgInfo message = new imgInfo();message.setimglocation(img);imgReceiver_crique.produce(message);} else {din.close();in.close();cs.close();while (imgCount.get() > 0) {}streamIsEnd = true;System.out.println("streamisend is true");}} else {din.close();in.close();cs.close();while (imgCount.get() > 0) {System.out.println("imgcount.get > 0    ");}streamIsEnd = true;}} catch (IOException e) {e.printStackTrace();}}// if 10 end}// while( !streamIsEnd ) end} catch (SocketException e) {try {din.close();in.close();cs.close();while (imgCount.get() > 0) {}} catch (IOException e1) {e1.printStackTrace();}} catch (IOException e) {e.printStackTrace();}}// run end}// imgReceiver endpublic static class imgShower extends Thread {private CriticQueue imgShower_crique = null;imgShower(CriticQueue q) {this.imgShower_crique = q;}public void run() {while (true) {System.out.println("imgshower run while(true)   ");if (!state) {System.out.println("state == true   ");while (imgCount.get() < 1) {}try {System.out.println("imgcount.get() < 1     ");System.out.println("imgshower try function");imgShower_crique.consume();} catch (IOException e) {e.printStackTrace();}}}}}// imgShower end}


现在客户端与服务器端都写好,运行的步骤就是:

1:打开服务器程序:java project的话就直接运行,android project的话就是运行后点击手机的菜单按钮,就会出现两个按钮,点击左边的开启服务器端(端口号目前设置的是12345,假设服务器的IP地址是192.168.0.100)

2:运行客户端程序:你可以在android程序中添加一个editview来接受我们要传给RunRootCommand函数的参数,这个自己去写就是了,最终达到的效果就是如下

 RunRootCommand("/system/whilecap  192.168.0.100  12345");

3:结束:点击服务器端的关闭服务器端,此时不再传送图片,但是客户端程序还在运行,需要去任务管理器中强行结束。因为此程序还没有优化,只是一个雏形。

到此,android的无线传屏就搞定了。

最后,还是希望大家帮帮忙,怎么去提升性能,怎么尽可能地增加每秒截取(截取与编码)的图片。










更多相关文章

  1. 如何为平板电脑开发android应用程序?
  2. Android(安卓)闹钟详解
  3. Android(安卓)NDK 是什么
  4. 05_Content Provider
  5. 在手机上开发程序之AIDE(Android集成开发环境)介绍
  6. Android(安卓)4.0的图形硬件加速及绘制技巧
  7. 应用程序基础(Application Fundamentals)
  8. [原]Android应用程序发送广播(sendBroadcast)的过程分析
  9. Android存储之SharePreferences

随机推荐

  1. IT行业的你可以看看
  2. Android布局类型资源(二)---XML、drawabl
  3. 在 Android(安卓)P 中使用默认 TLS 来保
  4. 野人学Android基础篇之网络通讯第二课--
  5. 随Android生命周期解绑Rxjava订阅的简单
  6. 我在Android开发中遇到的坑之微博正文点
  7. Android中AS创建点9图片与使用
  8. 源码阅读分析 - View的Touch事件分发
  9. 最火的Android开源项目(二)
  10. 基于Android的记事本的设计与开发(实验报