安卓fog求好友

Monsters Mojo下载_Monsters Mojo安卓版下载_Monsters Mojo 1.2.1手机版免费下载- AppChina应用汇
Monsters Mojo
用户对 Monsters Mojo 的评论
亲,想发表评论请下载哦~
我们的产品
客服邮箱:
微博/微信合作QQ:
网游玩家客服QQ:
24小时举报电话号码:
广告合作QQ:
游戏合作QQ:
帮助与支持
北京掌汇天下科技有限公司 版权所有
京公网安备39号|
京ICP备号-1
抵制不良游戏 拒绝盗版游戏 注意自身保护 谨防受骗上当 适度游戏益脑 沉迷游戏伤身 合理安排时间 享受健康生活
-提供安卓手机软件、游戏资源下载,做最贴心的Android手机软件应用平台!StringFog Project Url:
Introduction: 一款自动对字节码中的字符串进行加密 Android 插件工具 More: &&&&&& Tags:
一款自动对 dex 文件中的字符串进行加密 Android 插件工具,正如名字所言,给字符串加上一层雾霭,使人难以窥视其真面目,且支持在 app 和 sdk 中独立使用。
在 java 文件编译成 class 字节码后,class 字节码压缩成 dex 文件前,利用 ASM 库对 class 字节码中的字符串进行加密,同时将解密调用自动写进 class 字节码中,做到在运行时还原字符串内容,举个栗子:
原文:String a = &This is a string!&;
加密:String a = Decoder.decode(&ABCDEFGHIJKLMN&);
将 dex 中的字符串进行加密,可以提高反编译的难度,对于类似 appId、appKey 等敏感字符串,进行自动加密后,逆向应用时在使用 jadx 这一类反编译工具定位查找这些字符串的难度将加大,比如微信的 appId 前缀是&wx&,不加密基本上直接就可以搜索定位出来。当然,这里需要声明一点,没有绝对的安全,由于解密 Key 和算法是同样写在 dex 里面的,逆向时处理一下是可以还原出字符串内容的。另外,在运行时解密字符串会相应地降低性能,不过由于算法简单,影响不大。不支持 Instant Run演示
由于开发了 gradle 插件,所以在集成时非常简单,不会影响到打包的配置。插件已经上传到 jcenter,直接引用依赖就可以。
1、在根目录 build.gradle 中引入插件依赖
buildscript {
repositories {
dependencies {
classpath 'com.github.megatronking.stringfog:gradle-plugin:1.4.1'
2、在 app 的 build.gradle 中配置插件
apply plugin: 'stringfog'
stringfog {
key 'Hello World'
// 这是加解密 key,可以自由定义
enable true // 开关
fogPackages = ['com.xxx.xxx'] // 指定某些包下面的类进行加密
3、在 app 的 build.gradle 中引入加解密库依赖
dependencies {
compile 'com.github.megatronking.stringfog:lib:1.2.2'
如果开发者有不需要自动加密的类,可以使用注解 StringFogIgnore 来忽略:
@StringFogIgnore
public class Test {
修复使用 Java 8 时出现的 ZipException 编译错误
新增指定包名加密的配置项:fogPackages
移除指定包名不加密的配置项:exclude
修复 gradle 3.0+编译报错的 bug
修复 windows 下打包后报错的 bug
修复 windows 下文件分隔符的 bug
修复 applicationId 和 packageName 不一致导致无法编译的 bug
优化功能,不需要再手动 exclude 已使用 StringFog 的库
支持在 library 中使用,每个 library 可以使用不同 key
支持 exclude 指定包名不进行加密
修复一些已知 bug
Copyright (C) 2017, Megatron King
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.
loading Support MePaypal:
About MeGoogle+: GitHub: Blog:beifengfoguo的博客
https://blog.csdn.net/
https://static-blog.csdn.net/images/logo.gif
https://blog.csdn.net/beifengfoguo
https://blog.csdn.net/
https://blog.csdn.net/beifengfoguo/article/details/
https://blog.csdn.net/beifengfoguo/article/details/
beifengfoguo
SurfaceFlinger
1. SurfaceFlinger启动流程
SurfaceFlinger 线程启动是由加载init.rc文件后执行文件夹下的可执行文件启动函数
先见时序图
启动函数后创建自己的线程并限定线程最大为个初始话后加入线程池
ProcessState::self()-&setThreadPoolMaxThreadCount(4); // 设置最大线程数
sp&ProcessState& ps(ProcessState::self());
ps-&startThreadPool();
初始化的第一部分
Main函数调用的函数对等初始化
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(mEGLDisplay, NULL, NULL); // EGL 的初始化动作
sp&VSyncSource& vsyncSrc = new DispSyncSource(&mPrimaryDispSync, vsyncPhaseOffsetNs, true, "app"); // 虚拟化Vsync App UI部分
mEventThread = new EventThread(vsyncSrc);
sp&VSyncSource& sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync, sfVsyncPhaseOffsetNs, true, "sf"); // 虚拟化Vsync SF合成部分
mSFEventThread = new EventThread(sfVsyncSrc);
mEventQueue.setEventThread(mSFEventThread);
// Initialize the H/W composer object.
There may or may not be an
// actual hardware composer underneath.
mHwc = DisplayUtils::getInstance()-&getHWCInstance(this,
*static_cast&HWComposer::EventHandler *&(this));
// 初始化hardwarecomposer
// get a RenderEngine for the given display / config (can't fail)
mRenderEngine = RenderEngine::create(mEGLDisplay, mHwc-&getVisualID());
// retrieve the EGL context that was selected/created
mEGLContext = mRenderEngine-&getEGLContext();
LOG_ALWAYS_FATAL_IF(mEGLContext == EGL_NO_CONTEXT,
"couldn't create EGLContext");
(1) . EGL的初始就不说了有意思的是 实例了两个不同的与 一个是的另一个是合成的他们只是将层传过来的虚拟化了
他们根据sfVsyncPhaseOffsetNs 与 vsyncPhaseOffsetNs 两个相对偏移量将
app UI绘制 与 合成区分
其中的值可以改变这样做的目的是避免了因同时唤醒渲染和合成是造成对资源的抢占
(2) . 接下来启动上述
EventThread的构造函数只是初始化一些数据重要的是它的函数使用强指针时就会触发onFirstRef函数
void EventThread::onFirstRef() {
run("EventThread", PRIORITY_URGENT_DISPLAY+PRIORITY_MORE_FAVORABLE);
Run 启动其中一个重要的函数
( 3 ) . HWcomposer初始化
HWComposer::HWComposer(
const sp&SurfaceFlinger&& flinger, EventHandler& handler)
: mFlinger(flinger),mFbDev(0),
mNumDisplays(1),
mCBContext(new cb_context), mEventHandler(handler), mDebugForceFakeVSync(false)
// Note: some devices may insist that the FB HAL be opened before HWC.
int fberr = loadFbHalModule();
// 加载准备framebuffer hal模块, 最终加载gralloc.XX.so动态库
loadHwcModule();
// 加载准备硬件加速模块 最终会加载各平台对应的hwcomposer.XX.so动态库
if (needVSyncThread) {
// we don't have VSYNC support, we need to fake it
mVSyncThread = new VSyncThread(*this);
// Vsync周期控制
HWcomposer 的初始化其中两个很重要的工作就是加载与 模块它们为数据在硬件合成方面做准备工作
( 4 ) . 根据与 像素格式创建并获取当前
接下来看初始化的第二部分
// initialize our non-virtual displays
for (size_t i=0 ; i&DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
DisplayDevice::DisplayType type((DisplayDevice::DisplayType)i);
// set-up the displays that are already connected
if (mHwc-&isConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) {
// All non-virtual displays are currently considered secure.
bool isSecure =
createBuiltinDisplayLocked(type);
wp&IBinder& token = mBuiltinDisplays[i];
sp&IGraphicBufferProducer&
sp&IGraphicBufferConsumer&
BufferQueue::createBufferQueue(&producer, &consumer,
new GraphicBufferAlloc());
sp&FramebufferSurface& fbs = new FramebufferSurface(*mHwc, i,
consumer);
int32_t hwcId = allocateHwcDisplayId(type);
sp&DisplayDevice& hw = new DisplayDevice(this,
type, hwcId, mHwc-&getFormat(hwcId), isSecure, token,
fbs, producer,
mRenderEngine-&getEGLConfig());
if (i & DisplayDevice::DISPLAY_PRIMARY) {
// FIXME: currently we don't get blank/unblank requests
// for displays other than the main display, so we always
// assume a connected display is unblanked.
ALOGD("marking display %zu as acquired/unblanked", i);
hw-&setPowerMode(HWC_POWER_MODE_NORMAL);
// When a non-virtual display device is added at boot time,
// update the active config by querying HWC otherwise the
// default config (config 0) will be used.
int activeConfig = mHwc-&getActiveConfig(hwcId);
if (activeConfig &= 0) {
hw-&setActiveConfig(activeConfig);
mDisplays.add(token, hw);
( 1 ) . 初始化这个是生产者IGraphicBufferProducer 与 消费者IGraphicBufferConsumer 的关键桥梁
( 2 ) . 创建一个
( 3 ) . 初始化各个DisplayDevice, 每个显示设备都会封装为一个如等
DisplayDevice::DisplayDevice(
const sp&SurfaceFlinger&& flinger,
DisplayType type,
int32_t hwcId,
int format,
bool isSecure,
const wp&IBinder&& displayToken,
const sp&DisplaySurface&& displaySurface,
const sp&IGraphicBufferProducer&& producer,
EGLConfig config)
: lastCompositionHadVisibleLayers(false),
mFlinger(flinger),
mType(type), mHwcDisplayId(hwcId),
mDisplayToken(displayToken),
mDisplaySurface(displaySurface),
mDisplay(EGL_NO_DISPLAY),
mSurface(EGL_NO_SURFACE),
mDisplayWidth(), mDisplayHeight(), mFormat(),
mPageFlipCount(),
mIsSecure(isSecure),
mSecureLayerVisible(false),
mLayerStack(NO_LAYER_STACK),
mOrientation(),
mPowerMode(HWC_POWER_MODE_OFF),
mActiveConfig(0)
mNativeWindow = surface = new Surface(producer, false);
// 创建一个surface
ANativeWindow* const window = mNativeWindow.get(); // 获取一个本地窗口
char property[PROPERTY_VALUE_MAX];
* Create our display's surface
EGLSurface eglS
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (config == EGL_NO_CONFIG) {
config = RenderEngine::chooseEglConfig(display, format);
eglSurface = eglCreateWindowSurface(display, config, window, NULL);
eglQuerySurface(display, eglSurface, EGL_WIDTH,
&mDisplayWidth);
eglQuerySurface(display, eglSurface, EGL_HEIGHT, &mDisplayHeight);
// Make sure that composition can never be stalled by a virtual display
// consumer that isn't processing buffers fast enough. We have to do this
// in two places:
// * Here, in case the display is composed entirely by HWC.
// * In makeCurrent(), using eglSwapInterval. Some EGL drivers set the
window's swap interval in eglMakeCurrent, so they'll override the
interval we set here.
if (mType &= DisplayDevice::DISPLAY_VIRTUAL)
window-&setSwapInterval(window, 0);
mDisplay =
mSurface = eglS
mPageFlipCount = 0;
mViewport.makeInvalid();
mFrame.makeInvalid();
// virtual displays are always considered enabled
mPowerMode = (mType &= DisplayDevice::DISPLAY_VIRTUAL) ?
HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;
// Name the display.
The name will be replaced shortly if the display
// was created with createDisplay().
switch (mType) {
case DISPLAY_PRIMARY:
mDisplayName = "Built-in Screen";
case DISPLAY_EXTERNAL:
mDisplayName = "HDMI Screen";
mDisplayName = "Virtual Screen";
// e.g. Overlay #n
mPanelInverseMounted =
// Check if panel is inverse mounted (contents show up HV flipped)
property_get("persist.panel.inversemounted", property, "0");
mPanelInverseMounted = !!atoi(property);
// initialize the display orientation transform.
setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
#ifdef NUM_FRAMEBUFFER_SURFACE_BUFFERS
surface-&allocateBuffers();
初始化第三部分
// make the GLContext current so that we can create textures when creating Layers
// (which may happens before we render something)
getDefaultDisplayDevice()-&makeCurrent(mEGLDisplay, mEGLContext);
mEventControlThread = new EventControlThread(this);
mEventControlThread-&run("EventControl", PRIORITY_URGENT_DISPLAY);
// set a fake vsync period if there is no HWComposer
if (mHwc-&initCheck() != NO_ERROR) {
mPrimaryDispSync.setPeriod();
// initialize our drawing state
mDrawingState = mCurrentS
// set initial conditions (e.g. unblank default device)
initializeDisplays();
// 设置初始的layer数据
// start boot animation
startBootAnim();
// 开机动画设置
在完成之后会执行它的函数使之进入死循环去等待信号
至此的初始话工作完成但这才是开始的系统值得我们去深究去探索
作者:beifengfoguo 发表于
https://blog.csdn.net/beifengfoguo/article/details/
阅读:1174
https://blog.csdn.net/beifengfoguo/article/details/
https://blog.csdn.net/beifengfoguo/article/details/
beifengfoguo
Asynchronous Timewarp
ATW是一种中间帧生成技术,在游戏等场景中,保持帧率, 帮助减少画面颤抖。
但是该技术并不是万能的, 还是存在一定的局限性。
该技术已经在三星上有一定的经验基础, 本篇文章将简单介绍的帮助及缺陷。
时间扭曲,
异步时间扭曲,
时间扭曲是一种在一帧图片显示到屏幕上之前将其扭曲的技术, 目的是为了减少因头部转动引起场景变化产生画面绘制延迟。
在时间扭曲中最基本把握的就是头部的旋转 方向, 所以在上有较好的优势, 在做扭曲的过程中不会,消耗太多的性能。
Asynchronous timewarp异步时间扭曲是指平行于渲染操作的另一个线程上(即,异步)。在每一个之前,线程根据渲染线程最新的帧来算出一个新的扭曲帧。
VR为产生更好的用户体验, 图像的更新必须要与同步, 但是, 如果某一帧渲染时间过长,下一帧就会被丢掉, 引起画面抖动。这是由于一个新的帧还未被渲染完成,即还没有推送到视频显示器, 此时显示设备就会扫描到与上一帧相同的
如下图,到连续显示两帧的位置图:
这里,眼睛转动到左侧。当再次显示相同的图像时,它的光线落在视网膜上的不同部分,导致双图像抖动。
此时就会用来解决该问题:
如果被渲染的帧没有在下一个到来之前提交, 就会截获并扭曲最新一帧来代替未被渲染完成的帧, 虽然新的不是完全正确的帧, 但是与没有相比, 明显较少了画面的抖动。
作者:beifengfoguo 发表于
https://blog.csdn.net/beifengfoguo/article/details/
阅读:1186
https://blog.csdn.net/beifengfoguo/article/details/
https://blog.csdn.net/beifengfoguo/article/details/
beifengfoguo
的整体架构:
第一层是应用层:代表上层使用的接口注册或使用一个:
Sensor sensor = sensorManager.getDefaultSensor(sensortype);
sensorManager.registerListener(mListener, sensor, SensorManager.SENSOR_DELAY_FASTEST);
第二层层: 包含, 接收层传上来的 数据,并让应用层做出相应的动作,如陀螺仪转动,横竖屏切换等
第三层层: 层的硬件驱动等, 有的函数读取数据
二.分析的启动流程:
在系统中每一个功能点都是由来调度, 如熟知的, 等等, 这些我会在以后的博客中逐个分析
先看调用时序图
SystemServer中唯一的一个调用启动
SystemServer.java :
* Start the sensor service.
private static native void startSensorService();
com_android_server_SystemServer.cpp:
void* sensorInit(void *arg) {
ALOGI("System server: starting sensor init.\n");
// Start the sensor service
SensorService::instantiate();
ALOGI("System server: sensor init done.\n");
return NULL;
使用机制将启动。
SensorService的初始化:
Service的初始在SensorService::onFirstRef函数中
SensorDevice& dev(SensorDevice::getInstance());
// 实例化SensorDevice
if (dev.initCheck() == NO_ERROR) {
sensor_t const*
ssize_t count = dev.getSensorList(&list);
// 获取sensor list
if (count & 0) {
ssize_t orientationIndex = -1;
bool hasGyro = false, hasAccel = false, hasMag =
mLastEventSeen.setCapacity(count);
for (ssize_t i=0 ; i& i++) {
registerSensor( new HardwareSensor(list[i]) );
// 向HAL层注册触感器
mNextSensorRegIndex = 0;
for (int i = 0; i & SENSOR_REGISTRATIONS_BUF_SIZE; ++i) {
mLastNSensorRegistrations.push();
mInitCheck = NO_ERROR;
mAckReceiver = new SensorEventAckReceiver(this);
mAckReceiver-&run("SensorEventAckReceiver", PRIORITY_URGENT_DISPLAY);
run("SensorService", PRIORITY_URGENT_DISPLAY);
}SensorService的初始化主要有:
1. 创建SensorDevice实例,获取Sensor列表
2. 调用SensorDevice.getSensorList(),获取Sensor模块所有传感器列表并为为每个传感器注册监听器
SensorDevice的初始化:
SensorDevice::SensorDevice()
mSensorDevice(0),
mSensorModule(0)
status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
(hw_module_t const**)&mSensorModule);
if (mSensorModule) {
err = sensors_open_1(&mSensorModule-&common, &mSensorDevice);
if (mSensorDevice) {
if (mSensorDevice-&common.version == SENSORS_DEVICE_API_VERSION_1_1 ||
mSensorDevice-&common.version == SENSORS_DEVICE_API_VERSION_1_2) {
sensor_t const*
ssize_t count = mSensorModule-&get_sensors_list(mSensorModule, &list);
mActivationCount.setCapacity(count);
for (size_t i=0 ; i&size_t(count) ; i++) {
mActivationCount.add(list[i].handle, model);
mSensorDevice-&activate( reinterpret_cast&struct sensors_poll_device_t *&(mSensorDevice),list[i].handle, 0);
}SensorService 及的启动主要实现一下几件事:
1. 调用层的方法,加载模块文件
2. 调用的方法打开设备
3. 调用对模块使能
启动一个线程,获取层的数据:
SensorService::threadLoop():
bool SensorService::threadLoop()
SensorDevice& device(SensorDevice::getInstance());
const size_t vcount = mVirtualSensorList.size();
const int halVersion = device.getHalDeviceVersion();
ssize_t count = device.poll(mSensorEventBuffer, numEventMax);
// 从HAL层读取数据
// Reset sensors_event_t.flags to zero for all events in the buffer.
for (int i = 0; i & i++) {
mSensorEventBuffer[i].flags = 0;
SortedVector& sp&SensorEventConnection& & activeC
populateActiveConnections(&activeConnections);
// Send our events to clients. Check the state of wake lock for each client and release the
// lock if none of the clients need it.
bool needsWakeLock =
size_t numConnections = activeConnections.size();
for (size_t i=0 ; i & numC ++i) {
if (activeConnections[i] != 0) {
activeConnections[i]-&sendEvents(mSensorEventBuffer, count, mSensorEventScratch,
mMapFlushEventsToConnections);
// 向客户端发数据
needsWakeLock |= activeConnections[i]-&needsWakeLock();
// If the connection has one-shot sensors, it may be cleaned up after first trigger.
// Early check for one-shot sensors.
if (activeConnections[i]-&hasOneShotSensors()) {
cleanupAutoDisabledSensorLocked(activeConnections[i], mSensorEventBuffer,
进入循环, 不停的从底层数据, 并到上层
三. 客户端与服务端之间Sensor数据传递
先看服务器与客户端状态图:
1. 上图涉及两个线程
橘黄色服务端: 它通过不停的从获取数据
蓝色客户端:从中读数据
2. 传递数据是的一个关键宏:
// sensor 的version
//sensor 标识每个sensor都唯一
// sensor 的类型一个sensor可有多个类型 如: 陀螺仪有校准后与校准前类型
int32_t reserved0;
// sensor产生时间戳
sensors_vec_
//加速sensor
sensors_vec_
// 磁场感应sensor
sensors_vec_
// 横竖屏sensor
sensors_vec_
// 陀螺仪sensor
//温度sensor
// 距离sensor
// 光感sensor
//压力sensor
relative_ // 相对湿度sensor
uncalibrated_event_t uncalibrated_
// 无定向的陀螺仪sensor
uncalibrated_event_t uncalibrated_
// 无定向的磁场sensor
heart_rate_event_t heart_
// 心率检测sensor
meta_data_event_t meta_
step_ // 步数检测sensor
uint32_t reserved1[3];
} sensors_event_t;
4. 使用管道传递数据的原因是由于数据量较大, 不能胜任此任务, 所以需要管道来传递这些数据
5. 数据时序图:
根据图示可以看出客户端与服务端通过建立链接, 通过管道进行数据传递。
BitTube init函数:
void BitTube::init(size_t rcvbuf, size_t sndbuf) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
// sine we don't use the "return channel", we keep it small...
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
fcntl(sockets[0], F_SETFL, O_NONBLOCK);
fcntl(sockets[1], F_SETFL, O_NONBLOCK);
mReceiveFd = sockets[0];
//socket[0]接受数据
mSendFd = sockets[1];
// socket[1]发送数据
mReceiveFd = -
ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
1. 使用socket[0] 来接收数据
2. 使用socket[1] 来发送数据
3.通过pipe(fds)创建管道,通过fcntl来设置操作管道的方式,设置通道两端的操作方式为O_NONBLOCK ,非阻塞IO方式,read或write调用返回-1和EAGAIN错误。
作者:beifengfoguo 发表于
https://blog.csdn.net/beifengfoguo/article/details/
阅读:1910 评论:4
https://blog.csdn.net/beifengfoguo/article/details/
https://blog.csdn.net/beifengfoguo/article/details/
beifengfoguo
1.配置环境:
Eclipse需要配置工具, 确保可以新建。
推荐工具现在网址:
2. 导入android源码
在源码目录下有文件, 可以参考。
具体步骤:
section Java & Code Style & formatter, then click on import and choose
development/ide/eclipse/android-formatting.xml
go into Java & Code Style & Organize Import,
then click on import and choose development/ide/eclipse/android.importorder
将这两个文件导入后, 将文件复制到源代码根目录一份。
更具文件中的路劲导入所需源码目录
新建java project 选择已有的项目导入:
点击完成, 并等待导入成功。
3. 开始调试
导入成功后进入选择进程
(一般情况下, 该界面会显示所有进程的进程号, 检测端口号等信息, 如下图:
一会儿调试的时候会使用到后面的 端口号,
若没有的话也不需要着急, 可以在终端输入 命令查找当前系统用户所占用的端口号, 是以端口开始的, 所以我们只需找相近的即可
咱们继续选择后, 进入代码区域,
右键,选择Debug As & Debug Configurations,
选择“Remote Java Application”, 双击或new一个 application
选择是你对应的端口号(一般为),但是我的为
输入等信息保存。
进入查看进程上是否有小爬虫, 若有则说明可以调试,
若没有,查看错误, 若是有拒绝连接, 等错误, 应该是你的端口号没有选择对,重新在中选择即可。
进入代码在你想要的地方加入断点,
恭喜你,成功
该方法是在下使用通过, 但是同样使用与等环境
该文章只是大致写了一下流程, 如有问题请谅解, 并可评论回复,谢谢
作者:beifengfoguo 发表于
https://blog.csdn.net/beifengfoguo/article/details/
https://blog.csdn.net/beifengfoguo/article/details/
https://blog.csdn.net/beifengfoguo/article/details/
beifengfoguo
先看数据库中各个表之间的关系图:
各表中的字段详细:
、 contacts表
该表存储了现有的contacts联系人(在Contacts Filter 中可以通过账户过滤出来的联系人),该表是由Contactsprovider 的ContactAggregator.java自动维护,用户不会对此表进行操作。该表保存了联系人的contact_id、联系次数times_contacted、最后一次联系的时间latest_time_contacted、是否含有号码has_phone_number、是否被添加到Favorites联系人starred等信息。contacts是由raw contacts经过整合而来的,一个contacts可以由一个或多个raw
contacts组合而成。raw_contacts将关联或删除之后的联系人等信息处理之后,然后将数据整合到contacts表(该操作在contactprovider中自动完成)。
、raw_contacts表
保存了所有联系人的基本信息(包括各个账户的联系人,被删除的信息,关联之前的联系人信息等),每个联系人占一行,表里有一列deleted标识该联系人是否被删除(字段值为1:删除, 否则为0),该表保存的两个ID值: raw_contact_id和contact_id,从而将contacts表和raw_contacts表联系起来。该表保存了联系人是否只读raw_contacts_read_only、联系人修改次数version、是否被添加到Favorites联系人starred、显示的名字display_name、用于排序的首字母和汉语拼音sort_key等信息。
若联系人的deleted列为1时,raw_contacts在进行同步联系人的时候,service会将删除掉该表中对应的数据!!!
该记录被删除之后触发 raw_contacts触发器执行删除data等其他表中对应的数据:
保存了所有联系人的详细信息,每个字段占一行,该表通过raw_contact_id字段将data表和raw_contacts表联系起来。联系人的所有信息保存在列data1至data15中,该15列内容根据mimetypes_id与mimetypes 表相关联。其中包含邮箱信息,电话信息,是否有头像等内容,各列中保存的内容根据mimetype_id的不同而不同。
该表存放手机联系人账户信息,
Android通过不同账户来管理手机联系人, 如:本地联系人账户(Phonecontacts),SIM card账户(单卡:SIM account, 双卡:SIM1 account、SIM2 account), Facebook 账户(Facebook account) 等
该表保存关联联系人的信息, 通过连个raw_contacts_id 进行关联, 用户可以使用name关联或 phone number关联。
该表存储用户的通话记录,表中保存了联系人的姓名, 通话日期,号码类型(家庭,工作等),显示格式等信息。
Phonebook通过该表获取到用户最近一段时间的通话记录,此表是由framework层的拨打电话app进行的增操作,若用户在call log 中删除单条或多条记录时,该表中的记录也相应删除。 该表中的name 字段通过与联系人的contacts表关联,判断是否该号码存储到了手机。
delete_contacts:
该表存放了被用户删除的联系人, 在raw_contacts表里的
delete 字段控制, 若为删除则为1, 否则为0. 该表的清除数据会在触发一个删除联系人之后系统会删除最近一段时间的联系人
存放联系人组的信息。该表通过联系人账户,新建不同的联系人组,即一个联系人账户可以建多个组, 每个组对应一个唯一的 _id, title 等信息,用户将联系人增加到某组后,系统会建立视图,将group_id与data_id 关联进行查询。
该表存放快捷拨号的信息,它与data 表关联之后,可以将data表里的某个号码数据设置为快捷号码。
该表存放了不同账户是否可以同步等信息。
该表存放联系人的数据类型, 包括
手机号码:vnd.android.cursor.item/phone_v2
联系人姓名:vnd.android.cursor.item/name
邮箱:vnd.android.cursor.item/email_v2
作者:beifengfoguo 发表于
https://blog.csdn.net/beifengfoguo/article/details/

我要回帖

更多关于 fog的形容词 的文章

 

随机推荐