从Android源代码来看WiFi直连

什么是WiFi直连通俗点说,它可以不通过网络,也不通过蓝牙,只要两台设备都支持WiFi直连,打开WiFi,不用连接任何WiFi,就可以进行信息的传输(请忽略下面两张图中的WiFi连接标志,因为其与WiFi的连接与否无关,打开就可以)。在Android的设置->网络与互联网->WLAN-&
阅读更多

Android8.1原生系统网络感叹号消除

原生系统Android8.1上,WiFi上出现感叹号,此时WiFi可正常访问。原因这是Android 5.0引入的网络评估机制:就是当你连上网络后,会给目标产生204响应的服务器发送给一个请求,如果服务器返回的是状态码为204的响应,那么就被认为网络可以访问;否则,如返回的是其他状态码,那么将被视为
阅读更多

libstreaming局域网构建Android相机实时流媒体流程分析

这是一个涉及东西比较多的第三方库,里面的一些代码细节有点让人云里雾里。如果真的是这样,那说明了解的东西还太少,真正的去了解这些详细的东西,起码得知道相应的概念,如RTSP、RTP、H264、H264打包。我觉得首要的事是将大体的逻辑打通,然后再慢慢深入代码的细节,了解相关的技术与知识。奈何,这部分在
阅读更多

System.loadLibrary()进程不断重启

解决这个问题花费了最近很长的一段时间,所以觉得非常有写出来分享的必要。其实这个问题,主要还是需要考虑细心!因为说到底是权限的问题,但是又与平常所说的权限问题有很大的差别。网上遇到的一些关于加载so库的问题都是什么连接问题、什么又找不到了的之类,都是可以正常吐出Exception的情况,这个连个Exc
阅读更多

EventBus3的基本使用指南

使用EventBus可以省略繁杂的Handler,效果也是类似的,使用方法也很简单。这里从官网上面做一个小小的总结,可以看得更全面一些。入门贴官方教程定义事件事件就是一个普通的Java类,POJO就好。public class MessageEvent { public final Strin
阅读更多

关于Bitmap相关的一些总结

如何从当前View获取到Bitmap

1
2
3
v.setDrawingCacheEnabled(true);
v.buildDrawingCache();
Bitmap bitmap = v.getDrawingCache();

如何从TextureView中获取Bitmap

1
mTextureView.getBitmap();

如何压缩Bitmap

压缩成适配目标宽、高的Bitmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* get a scaled bitmap from a file located in path, adjust to destWidth and destHeight
* @param path the bitmap's file location
* @param destWidth destination width
* @param destHeight destination height
* @return a scaled bitmap
*/
public static Bitmap getScaledBitmap(String path, int destWidth, int destHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);

float srcWidth = options.outWidth;
float srcHeight = options.outHeight;

int inSampleSize = 1;
if (srcHeight > destHeight || srcWidth > destWidth){
float heightScale = srcHeight / destHeight;
float widthScale = srcWidth / destWidth;

inSampleSize = Math.round(heightScale > widthScale ? heightScale : widthScale);
}

options = new BitmapFactory.Options();
options.inSampleSize = inSampleSize;

return BitmapFactory.decodeFile(path, options);
}

如何将Bitmap与byte[]相互转换

从Bitmap到byte[]

1
2
3
4
int bytes = bmp.getByteCount();
ByteBuffer buf = ByteBuffer.allocate(bytes);
bmp.copyPixelsToBuffer(buf);
byte[] byteArray = buf.array();

从byte[]到Bitmap

1
2
3
Bitmap stitchBmp = Bitmap.createBitmap(width, height, type);
stitchBmp.copyPixelsFromBuffer(ByteBuffer.wrap(byteArray));
imageView.setImageBitmap(stitchBmp);

缩略图相关

理解ThumbnaiUtils

安卓系统应用调试脚本

这是一个对之前写的脚本的记录档案,自己也看不太懂当时的写法了,羞愧。用一个 repo 放这些脚本代码感觉有些浪费,干脆整个文章记录曾经的那段历史吧!

这代码的主要目的就是想少敲点命令,一步到位,做一个快男!

说明 - AndroidROMTools

Android run script tools for debugging system apps or frameworks. This was used by myself when debugging system apps or frameworks. In this project, module names was not given to avoid the project name, but the main structure was reminded.

此脚本主要分成一个入口与两个模块,详细的树状图如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.
├── README.md
├── btool
├── modules
│   ├── handleFramework.sh
│   ├── handleXxxx.sh
│   └── handleXxxxxx.sh
└── utils
├── handleSpecificTask.sh
├── init.sh
├── printOperationTip.sh
├── tipWhenNotImplement.sh
└── tipWhenParametersError.sh

2 directories, 10 files

入口

名字叫 btool,为啥起这个怪名字,我也不知道。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/bin/bash
# initialize variables
var1=$1
var2=$2
var3=$3
UTILS_DIR=~/.PersonalTools/utils
MODULES_DIR=~/.PersonalTools/modules

case $var1 in
"init" )
${UTILS_DIR}/init.sh
;;
"log" )
adb logcat -b main \*:E | grep "TAG"
;;
"wxxxx" )
${MODULES_DIR}/handleXXXX.sh $var2 $var3
;;
"txxxx" )
${MODULES_DIR}/handlexxxxxx.sh $var2 $var3
#${UTILS_DIR}/tipWhenNotImplement.sh $var1
;;
"txxxx" )
${UTILS_DIR}/tipWhenNotImplement.sh $var1
;;
"mxxxx" )
${UTILS_DIR}/tipWhenNotImplement.sh $var1
;;
* )
#${UTILS_DIR}/tipWhenParametersError.sh init xxxxxx *xxxxxx* *xxxxxx* *xxxxxx*
if [ "${var1}" == "" ]; then
${UTILS_DIR}/tipWhenParametersError.sh init xxxx *xxxx* *xxxxxx* *xxxxxx*
elif [ -f ${MODULES_DIR}/$var1.sh ]; then
#${UTILS_DIR}/tipWhenParametersError.sh init xxxxxx *xxxxxx* *xxxxxx* *xxxxxx*
${MODULES_DIR}/$var1.sh
else
${UTILS_DIR}/tipWhenParametersError.sh init xxxxxx *txxxxxx* *txxxxxx* *mxxxxxx*
${UTILS_DIR}/tipWhenNotImplement.sh "${var1}"
fi
;;
esac
exit

Utils

  • 初始化:init.sh

    1
    2
    3
    #!/bin/bash
    adb root
    adb wait-for-device remount
  • 打印带颜色的字符:printOperationTip.sh

    1
    2
    3
    4
    5
    6
    #!/bin/bash
    #echo -e "\e[31;43m-----------------------------------------------------------\e[0m"
    echo ""
    echo -e "\e[31;47m\t"$1"\t\e[0m"
    echo ""
    #echo -e "\e[31;43m-----------------------------------------------------------\e[0m"
  • 模块未完成的告警提示:tipWhenNotImplement.sh

    1
    2
    #/bin/bash
    echo -e "\nModule < \e[41m$1\e[0m \e[31m>NOT IMPELEMNT!\e[0m\n"
  • 参数错误提示:tipWhenParametersError.sh

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #/bin/bash
    #clear
    echo ""
    echo -e "\e[31;42mOops! Please give me the \e[0m\e[1;5;40;31mcorrect\e[0m\e[31;42m parameters!!!\e[0m"
    echo ""

    params=""
    cnt=1;
    for i in $@;
    do
    if [ $cnt != 1 ]; then
    params=$params",\e[1;5;31;40m "$i" \e[0m"
    else
    params="\e[1;5;31;40m "$i" \e[0m"
    fi
    cnt=$(($cnt+1))
    done
    if [ $cnt == 1 ]; then
    params="\e[4;31mNONE\e[0m"
    fi
    echo -e "\e[33mavaliable parameters are :\e[0m" $params"\n"
  • 处理特定任务:handleSpecificTask.sh

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    #!/bin/bash

    UTILS_DIR=~/.PersonalTools/utils

    OP_INDEX=$1
    PROJECT_SRC_DIR=$2
    APP_SRC_DIR=$3
    APP_OUT_DIR=$4
    APP_REMOTE_DIR=$5
    APP_PKG_NAME=$6
    PROJECT_LUNCH_IDX=$7

    #for i in $@; do
    #statements
    # echo $i
    #done

    # for compile SnapCamera APK in


    #if [ -f /tmp/btoollog ]; then
    #echo "exist"
    #else
    #echo "/tmp/btoollog not exists"
    #mkdir /tmp/btoollog
    #fi

    function compileModifiedSourceCode(){
    ${UTILS_DIR}/printOperationTip.sh "<Prepare to Compile>"
    cd ${PROJECT_SRC_DIR}
    pwd
    source build/envsetup.sh
    lunch ${PROJECT_LUNCH_IDX}
    cd ${APP_SRC_DIR}
    pwd
    mm
    }

    function makeSureCloseCompletelyAndReopen(){
    ${UTILS_DIR}/printOperationTip.sh "<Prepare to force-stop APK>"

    echo -e "\n\e[32m\ttry to stop app\e[0m"
    adb shell am force-stop ${APP_PKG_NAME}
    echo -e ""

    echo -e "\n\e[32m\ttry to start app\e[0m"
    adb shell am start -n ${APP_PKG_NAME}
    echo -e ""
    }

    function pushCompiledAPK2Phone(){
    ${UTILS_DIR}/printOperationTip.sh "<Prepare to Push APK>"
    cd ${APP_OUT_DIR}
    #pwd
    #echo "${APP_OUT_DIR}"
    adb push . ${APP_REMOTE_DIR}
    #adb push oat/arm64/$appname.odex /system/priv-app/$appname/oat/arm64/$appname.odex
    if [ $? -eq 0 ]; then
    echo -e "\n\e[32m\tpush app to phone successfully\e[0m"
    fi
    echo -e ""
    }

    function checkPushedFilesStatus(){
    ${UTILS_DIR}/printOperationTip.sh "<Prepare to Check Push Result>"
    adb shell ls -l ${APP_REMOTE_DIR} ${APP_REMOTE_DIR}/oat/arm64/
    if [ $? -eq 0 ]; then
    echo -e "\n\e[32m\tconnect to phone successfully\e[0m"
    fi
    echo -e ""
    }

    case $1 in
    "0" )
    compileModifiedSourceCode
    pushCompiledAPK2Phone
    makeSureCloseCompletelyAndReopen
    checkPushedFilesStatus
    ;;
    "1" )
    compileModifiedSourceCode
    ;;
    "2" )
    pushCompiledAPK2Phone
    ;;
    "3" )
    makeSureCloseCompletelyAndReopen
    ;;
    "4" )
    checkPushedFilesStatus
    ;;
    "subl" )
    subl ${APP_SRC_DIR}
    ;;
    "src" )
    echo ${APP_SRC_DIR}
    ;;
    * )
    ${UTILS_DIR}/tipWhenParametersError.sh 0 1 2 3 4 subl src
    echo -e "\t\e[1;4;5;31;40m 0 \e[0m\t : \e[33m1 -> 2 -> 3 -> 4\e[0m"
    echo -e "\t\e[1;4;5;31;40m 1 \e[0m\t : \e[33mCompile Modified Source Code\e[0m"
    echo -e "\t\e[1;4;5;31;40m 2 \e[0m\t : \e[33mPush Compiled APK 2 Phone\e[0m"
    echo -e "\t\e[1;4;5;31;40m 3 \e[0m\t : \e[33mClose App\e[0m"
    echo -e "\t\e[1;4;5;31;40m 4 \e[0m\t : \e[33mShow App Info\e[0m"
    echo -e "\t\e[1;4;5;31;40m subl \e[0m\t : \e[33mOpen App Src With Sublime Text\e[0m\n"
    echo -e "\t\e[1;4;5;31;40m src \e[0m\t : \e[33mPrint SRC path\e[0m\n"
    ;;
    esac

模块

  • Android Framework 处理:handleFramework.sh

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #!/bin/bash
    SRC_HOME=/home/ckt/work/xxxxxx/msm8909_go/LINUX/android/frameworks/base
    OUT_HOME=/home/ckt/work/xxxxxx/msm8909_go/LINUX/android/out/target/product/msm8909go/system/framework

    cd /home/ckt/work/xxxxxx/msm8909_go/LINUX/android/

    source build/envsetup.sh
    lunch 29
    make framework
    make snod

    adb push ${OUT_HOME}/arm/boot.oat /system/framework/arm/
    adb push ${OUT_HOME}/arm/boot.art /system/framework/arm/

    adb push ${OUT_HOME}/framework.jar /system/framework/
    adb push ${OUT_HOME}/framework-res.apk /system/framework/

    adb reboot bootloader
  • 桌面时钟:handleXxxx.sh

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #/bin/bash

    UTILS_DIR=~/.PersonalTools/utils

    PROJECT_DIR="/home/ckt/work/android8"
    PROJECT_SRC_DIR=${PROJECT_DIR}
    PROJECT_OUT_DIR=${PROJECT_DIR}/out/target/product/msm8953_64
    PROJECT_LUNCH_IDX=39

    DESKCLOCK_SRC_DIR=${PROJECT_SRC_DIR}/packages/apps/DeskClock/
    DESKCLOCK_OUT_DIR=${PROJECT_OUT_DIR}/system/app/DeskClock/
    DESKCLOCK_PKG_NAME=com.android.deskclock
    DESKCLOCK_REMOTE_DIR=/system/app/DeskClock


    case $1 in
    "deskclock" )
    ${UTILS_DIR}/handleSpecificTask.sh $2 ${PROJECT_SRC_DIR} ${DESKCLOCK_SRC_DIR} ${DESKCLOCK_OUT_DIR} ${DESKCLOCK_REMOTE_DIR} ${DESKCLOCK_PKG_NAME} ${PROJECT_LUNCH_IDX}
    ;;
    * )
    ${UTILS_DIR}/tipWhenParametersError.sh deskclock
    ;;
    esac
  • 多媒体应用:handleXxxxxx.sh

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    #!/bin/bash

    UTILS_DIR=~/.PersonalTools/utils

    PROJECT_DIR="/home/ckt/work/xxxxxxx/LINUX/android"
    PROJECT_SRC_DIR="${PROJECT_DIR}"
    PROJECT_OUT_DIR="${PROJECT_DIR}/out/target/product/msm8909go"
    PROJECT_LUNCH_IDX=29

    SOUNDRECORDER_SRC_DIR="${PROJECT_SRC_DIR}/packages/apps/SoundRecorder/"
    SOUNDRECORDER_OUT_DIR="${PROJECT_OUT_DIR}/system/priv-app/SoundRecorder/"
    SOUNDRECORDER_PKG_NAME=com.android.soundrecorder
    SOUNDRECORDER_REMOTE_DIR=/system/priv-app/SoundRecorder

    GALLERY_SRC_DIR="${PROJECT_SRC_DIR}/packages/apps/Gallery2/"
    GALLERY_OUT_DIR="${PROJECT_OUT_DIR}/system/app/Gallery2/"
    GALLERY_PKG_NAME=com.android.gallery3d
    GALLERY_REMOTE_DIR=/system/app/Gallery2

    SNAPDRAGONMUSIC_SRC_DIR="${PROJECT_SRC_DIR}/packages/apps/Music/"
    SNAPDRAGONMUSIC_OUT_DIR="${PROJECT_OUT_DIR}/system/app/Music/"
    SNAPDRAGONMUSIC_PKG_NAME=com.android.music
    SNAPDRAGONMUSIC_REMOTE_DIR=/system/app/Music

    CLOCK_SRC_DIR="${PROJECT_SRC_DIR}/packages/apps/DeskClock"
    CLOCK_OUT_DIR="${PROJECT_OUT_DIR}/system/app/DeskClock"
    CLOCK_PKG_NAME=com.android.deskclock
    CLOCK_REMOTE_DIR=/system/app/DeskClock

    case $1 in
    "soundrecorder" )
    ${UTILS_DIR}/handleSpecificTask.sh $2 ${PROJECT_SRC_DIR} ${SOUNDRECORDER_SRC_DIR} ${SOUNDRECORDER_OUT_DIR} ${SOUNDRECORDER_REMOTE_DIR} ${SOUNDRECORDER_PKG_NAME} ${PROJECT_LUNCH_IDX}
    ;;
    "gallery" )
    ${UTILS_DIR}/handleSpecificTask.sh $2 ${PROJECT_SRC_DIR} ${GALLERY_SRC_DIR} ${GALLERY_OUT_DIR} ${GALLERY_REMOTE_DIR} ${GALLERY_PKG_NAME} ${PROJECT_LUNCH_IDX}
    ;;
    "music" )
    ${UTILS_DIR}/handleSpecificTask.sh $2 ${PROJECT_SRC_DIR} ${SNAPDRAGONMUSIC_SRC_DIR} ${SNAPDRAGONMUSIC_OUT_DIR} ${SNAPDRAGONMUSIC_REMOTE_DIR} ${SNAPDRAGONMUSIC_PKG_NAME} ${PROJECT_LUNCH_IDX}
    ;;
    "clock" )
    ${UTILS_DIR}/handleSpecificTask.sh $2 ${PROJECT_SRC_DIR} ${CLOCK_SRC_DIR} ${CLOCK_OUT_DIR} ${CLOCK_REMOTE_DIR} ${CLOCK_PKG_NAME} ${PROJECT_LUNCH_IDX}
    ;;
    * )
    ${UTILS_DIR}/tipWhenParametersError.sh soundrecorder gallery music clock
    ;;
    esac

目前这些脚本对我来说,已无实际用途,不能保证准确性,只是做一个纪念,纪念那段无知却在挣扎的时光。

Service使用笔记

看过好几回了,但是还是好像每次都忘,可能是用得少,但是工作中看的一些源代码中用的太多了,可是每次用的时候都看一遍,有点浪费时间,还是自己做一个简单的总结,这样可能以后会快一些。

这篇在草稿箱里实在是存太久了。。。


Service的分类

  • 启动型
    通过调用startService() 启动,一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。

  • 绑定型
    通过调用bindService() 绑定到服务,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行; 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁

服务在其托管进程的主线程中运行,它既不创建自己的线程,也不在单独的进程中运行(除非另行指定)。

Service的生命周期

因为其分两类,所以它的生命周期也有两种:
图片1

左边为启动型, 右边为绑定型.

Service在清单文件中的属性

  • android:exported: 顾名思义,是否可以被导出,即是否可以被绑定用做服务端.
    false:确保服务仅适用于您的应用.
    true: 与false相反.

请始终使用显式 Intent 启动或绑定 Service,且不要为服务声明 Intent 过滤器

启动型Service

继承Service

若使用这种方法,则需要注意一个问题,那就是**Service默认的线程是主线程**, 不要在其中做耗时的事情.

继承IntentService

使用这种方法, 就不需要担心线程的问题.因为其做为一个对Service的简单封装,它内部已经将所有的事件都放在了子线程中.不妨对其做一个简单的分析来加深对Service的理解.

1
2
3
4
5
6
7
8
9
10
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread =
new HandlerThread("IntentService[" + mName + "]");
thread.start();

mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}

在其onCreate()中,将开启一个初始化好Looper的子线程. 并为这个Looper的消息队列添加一个Handler.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//IntentService.java
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}

@WorkerThread
protected abstract void onHandleIntent(
@Nullable Intent intent);

其中的Handler用来处理在这个子线程中的事务,其中的onHandleIntent()正是我们在继承IntentService时所需要实现的一个方法, 也就是说我们在其中写的方法都是在子线程中执行.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}

/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

onStart()并没有太大的意义,只是一个普通的函数,重要的是它里面做的事情。它将从onStartCommand()里面得到的intent,通过message传递给handler,然后再是自己,对不同intent的处理。

onStartCommand()的返回值,有下面三种:

  • START_NOT_STICKY
    如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
  • START_STICKY
    如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。
  • START_REDELIVER_INTENT
    如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务。

如何停止

启动服务必须管理自己的生命周期。也就是说,除非系统必须回收内存资源,否则系统不会停止或销毁服务,而且服务在 onStartCommand() 返回后会继续运行。
因此,服务必须通过调用stopSelf() 自行停止运行,或者由另一个组件通过调用 stopService() 来停止它。一旦请求使用 stopSelf() 或 stopService() 停止服务,系统就会尽快销毁服务。

http://blog.csdn.net/baidu_31405631/article/details/52469093

绑定型Service

对于此种类型的,感觉自己用得到的地方并不是很多,但是要能够看得懂这这种类型的Service的代码,尤其是AIDL,在Android中非常常见。

AIDL的大致使用步骤如下:

  1. 编写包含接口的aidl文件,make project,生成同名的java文件
  2. AIDL是一种可跨进程的,就是说可以在进程A中,调用进程B中的方法,所以要首先要实现该方法类。实现其中的方法,只需要继承同名java文件中的Stub类,并实现其中的方法,即可。

ButterKnife官方使用指南

看到了ButterKnife之后,感觉它实在是太棒了,可以省略掉一大堆无趣的findViewById(),整个代码看起来都舒服多了。这篇使用说明来自它的官方网站的简易介绍,用起来非常简单,但是也是有挺多的情况,所以还是觉得自己翻译出来,方便以后查阅吧!


使用@BindViewID注解相应的变量,ButterKnife就会在你的layout文件中找到所对应的View并赋值给它。

1
2
3
4
5
6
7
8
9
10
11
12
class ExampleActivity extends Activity {
@BindView(R.id.title) TextView title;
@BindView(R.id.subtitle) TextView subtitle;
@BindView(R.id.footer) TextView footer;

@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
// TODO Use fields...
}
}

上面例子中,所生成的代码大致与下面代码等同:

1
2
3
4
5
public void bind(ExampleActivity activity) {
activity.subtitle = (android.widget.TextView) activity.findViewById(2130968578);
activity.footer = (android.widget.TextView) activity.findViewById(2130968579);
activity.title = (android.widget.TextView) activity.findViewById(2130968577);
}

资源绑定

使用@BindBool, @BindColor, @BindDimen, @BindDrawable, @BindInt, @BindString与一个对应的ID来绑定定义好的资源,

1
2
3
4
5
6
7
class ExampleActivity extends Activity {
@BindString(R.string.title) String title;
@BindDrawable(R.drawable.graphic) Drawable graphic;
@BindColor(R.color.red) int red; // int or ColorStateList field
@BindDimen(R.dimen.spacer) Float spacer; // int (for pixel size) or float (for exact value) field
// ...
}

非ACTIVITY绑定

我们还可以在已知View的情况下,在任意的对象中,绑定该View中所含有的控件。比如在Fragment中:

1
2
3
4
5
6
7
8
9
10
11
public class FancyFragment extends Fragment {
@BindView(R.id.button1) Button button1;
@BindView(R.id.button2) Button button2;

@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fancy_fragment, container, false);
ButterKnife.bind(this, view);
// TODO Use fields...
return view;
}
}

另外一个是在ViewHolder中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MyAdapter extends BaseAdapter {
@Override public View getView(int position, View view, ViewGroup parent) {
ViewHolder holder;
if (view != null) {
holder = (ViewHolder) view.getTag();
} else {
view = inflater.inflate(R.layout.whatever, parent, false);
holder = new ViewHolder(view);
view.setTag(holder);
}

holder.name.setText("John Doe");
// etc...

return view;
}

static class ViewHolder {
@BindView(R.id.title) TextView name;
@BindView(R.id.job_title) TextView jobTitle;

public ViewHolder(View view) {
ButterKnife.bind(this, view);
}
}
}

其它的绑定方式

可使用Activity当做一个根View可以绑定任何对象。如果你使用了MVC模式,你可以使用ButterKnife.bind(this, activity)来绑定Controller
可使用ButterKnife.bind(this)来绑定一个View里面的子View。如果你在layout文件中使用了<merge>标签并且在View的构造器中填充,你可以在这之后立马调用它。或者,你也可以在onFinishInflate()回调中调用。

VIEW LISTS

将所需要的控件,全部填充到一个List中。

1
2
@BindViews({ R.id.first_name, R.id.middle_name, R.id.last_name })
List<EditText> nameViews;

apply()方法可以对List中所有的View执行某个操作。

1
2
ButterKnife.apply(nameViews, DISABLE);
ButterKnife.apply(nameViews, ENABLED, false);

可以指定一些简单的动作。

1
2
3
4
5
6
7
8
9
10
static final ButterKnife.Action<View> DISABLE = new ButterKnife.Action<View>() {
@Override public void apply(View view, int index) {
view.setEnabled(false);
}
};
static final ButterKnife.Setter<View, Boolean> ENABLED = new ButterKnife.Setter<View, Boolean>() {
@Override public void set(View view, Boolean value, int index) {
view.setEnabled(value);
}
};

当然也可以在apply()方法中指定一个Android中控件的属性名。

ButterKnife.apply(nameViews, View.ALPHA, 0.0f);

绑定LISTENER

监听器也可以自动配置到相应的View上。

1
2
3
4
@OnClick(R.id.submit)
public void submit(View view) {
// TODO submit data to server...
}

监听器函数的参数都是可选的。

1
2
3
4
@OnClick(R.id.submit)
public void submit() {
// TODO submit data to server...
}

指定一个确定的类型,它将会被自动转换成之。

1
2
3
4
@OnClick(R.id.submit)
public void sayHi(Button button) {
button.setText("Hello!");
}

还可以为将一个监听器函数,绑定到多个控件上。

1
2
3
4
5
6
7
8
@OnClick({ R.id.door1, R.id.door2, R.id.door3 })
public void pickDoor(DoorView door) {
if (door.hasPrizeBehind()) {
Toast.makeText(this, "You win!", LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Try again", LENGTH_SHORT).show();
}
}

自定义View时,绑定自己的监听器函数不需要设置ID

1
2
3
4
5
6
public class FancyButton extends Button {
@OnClick
public void onClick() {
// TODO do something!
}
}

重置绑定

FragmentActivity的生命周期不同。当在FragmentonCreateView()中使用了绑定,就需要在onDestroyView()中将变量置为nullButterKnife在调用绑定时会返回一个Unbinder的实例,在适当的生命周期回调中,调用这个实例的unbind()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class FancyFragment extends Fragment {
@BindView(R.id.button1) Button button1;
@BindView(R.id.button2) Button button2;
private Unbinder unbinder;

@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fancy_fragment, container, false);
unbinder = ButterKnife.bind(this, view);
// TODO Use fields...
return view;
}

@Override public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

其它绑定

通常@Bind和监听器绑定都是必须的。如果在目标View中为找到相应ID的控件,则会抛出异常。

为了抑制住这中异常,创建一个可选的绑定,可以使用@Nullable@Optional来注解变量或方法。

注 : 可使用任何名为@Nullable的注解来注解变量,但推荐Android support-annotations中的@Nullable

1
2
3
4
5
@Nullable @BindView(R.id.might_not_be_there) TextView mightNotBeThere;

@Optional @OnClick(R.id.maybe_missing) void onMaybeMissingClicked() {
// TODO ...
}

多方法监听器

可在注解中加入参数来区分。

1
2
3
4
5
6
7
8
9
@OnItemSelected(R.id.list_view)
void onItemSelected(int position) {
// TODO ...
}

@OnItemSelected(value = R.id.maybe_missing, callback = NOTHING_SELECTED)
void onNothingSelected() {
// TODO ...
}

下载

GRADLE

1
2
compile 'com.jakewharton:butterknife:(insert latest version)'
annotationProcessor 'com.jakewharton:butterknife-compiler:(insert latest version)'

通过反射来获取Intent中的Key

在项目中遇到一种情况,我想通过了解某个Intent里面到底存了哪些数据来解决这个问题。但是我们知道,Intent里面的数据需要知道key才能调用相应的函数取出来,所以如何才能找出这些key呢?通过对Intent代码里面对存数据的观察,我们可以看到,通过putExtra()存的数据,都放在一个叫做mE
阅读更多