一路追梦想

热爱技术,更热爱生活


  • 首页

  • 关于

  • 标签

  • 归档

Android通过反射机制实现打开关闭通知栏

发表于 2015-03-18 | 分类于 Android

之前用iPhone的时候,iPhone的Assisitivetouch中有一个通知中心选项,点击通知中心就会自动打开通知栏,在手机屏幕越来越大的今天,这个功能还是很实用的,现在用Android手机了,想着是不是可以在Android手机上也实现一个类似的功能,研究了一下发现Android的打开通知栏的方法是没有对普通用户开放的,也就是说我们不能够直接在代码中去调用打开通知栏的方法,只能通过反射机制来获取打开通知栏的方法进行调用,反射打开通知栏代码如下:

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
private void handleNotify(String hanlde) {
int currentApiVersion = android.os.Build.VERSION.SDK_INT;
try {
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class
.forName("android.app.StatusBarManager");
Method expand = null;
if (service != null) {
if (currentApiVersion <= 16) {
expand = statusbarManager.getMethod("expand");
} else {
if (hanlde == "open") {
expand = statusbarManager
.getMethod("expandNotificationsPanel");
} else if (hanlde == "close") {
expand = statusbarManager
.getMethod("collapsePanels");
}
}
expand.setAccessible(true);
expand.invoke(service);
}
} catch (Exception e) {
Log.d("caobin", "error");
}
}

注意有三点需要注意:

  1. 在执行Object service = getSystemService(“statusbar”);这句的时候,可能会提示参数错误,说是参数只能是Context.xxxService之类的,忽略一下就好。

  2. 因为在Android4.1(API 16)之后,打开通知栏的方法名由expand改成expandNotificationsPanel了,所以为了保证程序在所有的手机上都能够正常工作,需要加上api的判断。

  3. 关闭通知栏的方法是collapsePanels,调用该方法会自动关闭打开的通知栏。

附上项目demo地址:反射方法调用打开关闭通知栏

Python之文件与异常

发表于 2015-03-01 | 分类于 Python

过完年了,新的一年希望能找份理想的工作,好了开始正题,今天读的是Python中的文件和异常

Python是如何从文件读取数据的呢,先看一段代码:

1
2
3
4
file = open('a.txt')
#Todo something with the file
...
file.close()

看完上面这段代码就基本明白,Python是通过open()这个BIF来和文件交互,结合for语句使用,就很容易实现从文件中读取数据了。

Python中和读取文件相关的几个方法的基本功能如下所示:

open()

BIF打开一个磁盘文件,创建一个迭代器从文件读取数据,一次读取一行

readline()

从一个打开的文件读取一行数据

seek()

可以用来将文件‘退回’到起始位置

close()

关闭一个之前打开的文件

split()

可以将一个字符串分解为一个子字串列表,可以指定一个最大分割数来限定最多分割为几部分,如果没有指定分隔符的话,默认以空格作为分隔符进行分割

find()

在一个字符串中查找一个特定子串,找到的话返回特定子串的起始位置,找不到返回-1

先看一下下面这段Python代码(注意:该Python文件的要和sketch.txt这一个目录)

1
2
3
4
5
6
7
8
data = open('sketch.txt')
for each_line in data:
(role,line_spoken) = each_line.split(':')
print(role,end='')
print(' said: ',end='')
print(line_spoken,end='')

data.close()

这段代码很简单,就是读取文件sketch.txt里面的内容,将每行以:分割,然后进行格式化输出,但是事实上这段代码是不完善的,因为如果sketch.txt里面某一行没有:字符亦或者某一行里面不止一个:字符,程序执行过程中都会出现ValueError错误,为了让程序变的更健壮,我们改一下,下面是2.0版本

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
data = open('sketch.txt')

for each_line in data:
if not each_line.find(':') == -1:
(role,line_spoken) = each_line.split(':',1)
print(role,end='')
print(' said: ',end='')
print(line_spoken,end='')

data.close()
```
2.0在1.0的基础上有两个改动:
<!--more-->
* 判断某一个有没有:这个符号,有的话再分割,防止某一行没有:而出错
* split传入第二个可选参数maxsplit(最大分割数),限制只分为两部分,防止:过多而出错

现在看上去程序是没有什么错误了,也能够正常输出了。但是问题还是来了,你会发现这样做的话如果后面我又想以,或者其他字符作为分隔符,改动的就多了,而且if语句感觉好像也很别扭,那么有没有更好的解决办法呢?我们一起看看3.0版本
```Python
data = open('sketch.txt')

for each_line in data:
try:
(role,line_spoken) = each_line.split(':',1)
print(role,end='')
print(' said: ',end='')
print(line_spoken,end='')
except:
pass

data.close()

有其他编程语言经验的一眼就看出来了,这不是异常捕获呢,对,这就是Python的异常捕获机制,简称try/except机制,try:后面跟你可能会抛出运行时错误的代码,except:后面跟错误恢复代码,上面是直接跟一个pass,相当于空语句,什么都不做。

这样一来,3.0版本的不但可以正常运行,而且兼容性还比较高,代码逻辑简单,是不错的选择。这个会不会还有其他错误呢,细心观察会发现open() BIF这里,如果文件不存在,会抛出IOError错误。为了解决错误,我们可以选择增加逻辑判定sketch.txt是否存在或者再加一层异常处理,前面已经对比过,增加异常处理相对比增加代码逻辑要好一些,所以我们选择再增加一层异常处理。

另一方面我们捕获异常的时候使用的是except:这种一般法的方式,这样无论什么样的异常或者错误都会被忽略,这并不是我们要的,因为我们知道现在会出现的错误只有外层open的IOError和字符串截取可能发生的ValueError这两种错误,我们只想针对这两种错误捕获异常,而不是忽略任何错误,这样不利于我们分析问题。

综合上面两点,最终版4.0如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try:
data = open('sketch.txt')

for each_line in data:
try:
(role,line_spoken) = each_line.split(':',1)
print(role,end='')
print(' said: ',end='')
print(line_spoken,end='')
except ValueError:
pass

data.close()

except IOError:
print('The data file is missing!')

tips:

Python中不可改变的常量列表称为元组(tuple)。一旦将列表数据赋值至一个元组,就不能再改变,元组是不可改变的

日常工作中Git常用命令(持续更新)

发表于 2015-02-01 | 分类于 Git

前言

因为之前工作中一直用的控制版本都是svn,现在改为用git了,不得不说git是非常强大的,自然而然功能多了,命令也就多了,有些常用的命令不是忘记就是和svn搞混淆,索性记录一下常用到的命令,用到什么了就记录一下,方便以后查看

恢复本地删除的文件

直接从本地把文件checkout出来就可以了,用不着从远程服务器上pull下来,因为,所以的历史版本你的本地都有的

处理冲突

如果系统中有一些配置文件在服务器上做了配置修改,然后后续开发又新添加一些配置项的时候,在发布这个配置文件的时候,会发生代码冲突:

1
2
3
error: Your local changes to the following files would be
overwritten by merge:protected/config/main.php.Please,commit your
changes or stash them before you can merge.

如果希望保留生产服务器上所做的改动,仅仅并入新配置项

1
2
3
git stash
git pull
git stash pop

然后可以使用git diff -w +文件名 来确认代码自动合并的情况.

如果希望用代码库中的文件完全覆盖本地工作版本

1
2
git reset --hard
git pull

其中git reset是针对版本,如果想针对文件回退本地修改,使用

1
git checkout HEAD file_to_restore

阅读全文 »

Python之列表

发表于 2015-01-31 | 分类于 Python

记得最开始学接触Python就被它那高效简洁的代码吸引了,就决定要学Python,那时候还没大学毕业,在网易云课堂看一些Python的视频,但是因为后来从事Android开发又加上工作比较忙,所以就没有继续系统学习和梳理了,之前学的也都忘差不多了,现在决定重新系统地再学习一遍,记得刚工作的时候买了一本Head First Python,一直放在抽屉了没怎么看,现在又派上用场了,坚持下去,就会成功

先给出一个列表的定义:

1
language = ["Python","Java","C/C++"]

列表定义:

如果你有其他高级语言的基础第一次接触Python的时候会很好奇,难道不需要为列表声明类型信息吗?答案是不用,Python的变量标识符没有类型,标识符只是名字,可以只是某个类型的数据对象,可以把Python的列表想象成是一个高层集合

关于列表在内存中存储:Python创建一个列表时,解释器会在内存中创建一个类似数组的数据结构来存储数据,数据堆自下而上堆放(形成一个堆栈)。和其他语言一样,默认第一项的编号也是从0开始。

可以通过print(language[0])在屏幕上打出列表第一项的值

Python列表的一些方法:

len()

获取列表长度

1
2
3
4
>>> len(language)
3
>>> print(len(language))
3

append()

在列表末尾增加一个数据项

1
2
3
>>> language.append("Swift")
>>> print(language)
['Python', 'Java', 'C/C++', 'Swift']

pop()

从列表末尾删除数据项

1
2
3
4
5
>>> language.pop()
'Swift'
>>> print(language)
['Python', 'Java', 'C/C++']
>>>

extend()

在列表末尾增加一个数据项集合

1
2
3
>>> language.extend(["Swift","Go"])
>>> print(language)
['Python', 'Java', 'C/C++', 'Swift', 'Go']

remove()

在列表中找到并删除一个特定的数据项:

1
2
3
>>> language.remove("Java")
>>> print(language)
['Python', 'C/C++', 'Swift', 'Go']

insert()

在某个特定位置前面插入一个数据项:

1
2
3
>>> language.insert(2,"Java")
>>> print(language)
['Python', 'C/C++', 'Java', 'Swift', 'Go']

列表中可以混入不同类型的数据以及列表中嵌套列表,给出一个嵌套列表的例子,然后输出列表中的每一项:

1
2
3
4
5
6
7
8
9
10
11
>>> movies = ["The Holy Grail",1975,"Terry Gilliam",91,["Graham Chapman",["Michael Palin","John Cleese","Terry Gilliam","Eric Idle","Terry Jones"]]]
>>> print(movies)
['The Holy Grail', 1975, 'Terry Gilliam', 91, ['Graham Chapman', ['Michael Palin', 'John Cleese', 'Terry Gilliam', 'Eric Idle', 'Terry Jones']]]
>>> for each_item in movies:
print(each_item)

The Holy Grail
1975
Terry Gilliam
91
['Graham Chapman', ['Michael Palin', 'John Cleese', 'Terry Gilliam', 'Eric Idle', 'Terry Jones']]

你会发现,其实只是把列表中的每一项整个打印出来了,但是因为列表的有些项本身还是一个列表,甚至列表中还有列表,那么我们需要改进程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> for each_item in movies:
if isinstance(each_item,list):
for each_value in each_item:
if isinstance(each_value,list):
for nested_item in each_value:
print(nested_item)
else:
print(each_value)
else:
print(each_item)

The Holy Grail
1975
Terry Gilliam
91
Graham Chapman
Michael Palin
John Cleese
Terry Gilliam
Eric Idle
Terry Jones

这样打印出来的就没有列表了,但是你会发现重复代码很多,而且如果嵌套多层的话,代码要变的更臃肿,就不符合Python的简洁高效了,所以后面我们会来优化这段代码

注意几点

  • 如果需要在一个字符串中嵌入一个双引号需要使用转义符:\ 像这样:\” 或者用单引号引起这个字符串。
  • Python中,单引号和双引号都可以用来创建字符串,有一个规则是:如果字符串前面使用了某个引号(单引号或双引号),那么字符串后面也要用同样的引号:不能再字符串前后混合使用不同的引号。
  • 可以通过help(insert)来查看BIF函数insert()方法的解释和用法

Android开机设置默认Launcher

发表于 2015-01-27 | 分类于 Android

讲到设置默认Launcher,必须先说说Provision.apk这个应用,这个是Android原生自带的应用,本文讲解设置默认Launcher就是通过Provision.apk这个应用来实现的

首先我们知道Android启动HomeLauncher的最后一步是在ActivityManagerService.java中的startHomeActivityLocked方法中发送一个带有android.intent.category.HOME
的intent来启动响应该属性的应用,一般Launcher的Manifest配置文件中都会有此属性,所以系统中的Launcher就是这样被启动了。关于Android启动Launcher的流程,找时间可以再写一篇文章分析,暂时介绍到这里

接下来就要说Provision这个应用了,该应用的源码在packages/apps/下的Provision这个文件夹中,看一下该应用的目录结构:

该应用只有一个java文件DefaultActivity.java,我们看一下该应用的配置文件AndroidManifest.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- For miscellaneous settings -->
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />

<application>
<activity android:name="DefaultActivity"
android:excludeFromRecents="true">
<intent-filter android:priority="1">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>

我们注意到其实Provision也会响应android.intent.category.HOME属性,并且从这句android:priority=1可以看出起优先级是比一般Launcher都要高的,所以在AMS的startHomeActivityLocked启动Launcher的时候会先启动Provision应用,然后才是启动HomeLauncher,那接着我们看看在Provision中都做了些什么,我们知道默认是启动DefaultActivity.java这个文件,看一下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Application that sets the provisioned bit, like SetupWizard does.
*/
public class DefaultActivity extends Activity {

@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);

// Add a persistent setting to allow other apps to know the device has been provisioned.
Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
Settings.Secure.putInt(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);

// remove this activity from the package manager.
PackageManager pm = getPackageManager();
ComponentName name = new ComponentName(this, DefaultActivity.class);
pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
// terminate the activity.
finish();
}
}

阅读全文 »

谈谈Android管理任务栈的方式

发表于 2015-01-18 | 分类于 Android

之前在研究Android 四种LaunchMode的时候,有看到关于taskAffinity的内容,当时没有细看,现在突然发现同事提交代码的时候有写关于修改taskAffinity的东西,于是就有抽时间详细的研究了一下taskAffinity这个属性,发现还蛮复杂的,把自己的一些实践和见解写下来,供以后查看,涉及到的内容较多,可以重点查看红色的字体。

学习技术最好的途径就是Google官方文档加代码实践,在开始说taskAffinity属性之前,我们可以先看一下官网对于Task的定义:

1
A task is a collection of activities that users interact with when performing a certain job. The activities are arranged in a stack (the back stack), in the order in which each activity is opened.

简单来说就是:task就是一组特定的activities集合,这些activities按照每一个被打开的顺序放到一个stack中。

阅读全文 »

DrawerLayout导航抽屉的使用

发表于 2015-01-01 | 分类于 Android

现在很多移动APP都支持那种左划或者右划调出抽屉选项的操作,目前第三方库比较知名的是配合ActionBarShelock(Github地址,官网地址)使用的SlidingMenu(Github地址),这个开源库很强大,也有很多APP都在使用,经过测试和验证过的,大家都可以放心使用

今天要说的是google官方出的DrawerLayout控件的使用,先看两张效果图:

根据Google官方文档可以知道,使用DrawerLayout控件一般需要下面这些步骤:

创建一个DrawerLayout布局

这个比较简单,要求DrawerLayout为根布局,第一个子布局代表没有打开抽屉时候的主视图(这个视图的宽度必须为match_parent),第二个子布局就代表抽屉布局了,一般抽屉中放入的都是一些设置选项,以ListView为常见布局.代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 主视图布局,必须是DrawerLayout的第一个子布局,且宽度要充满父级容器 -->

<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" /&gt;
<!-- 抽屉视图布局 -->
<ListView
android:id="@+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:background="#c2e"/>
</android.support.v4.widget.DrawerLayout>

需要特别说明的是抽屉视图中的layout_gravity属性代表抽屉的打开方向,start代表从左边打开,end代表从右边打开,当然如果你的sdk api小于17的话,可以使用left和right可以达到同样的效果

初始化抽屉布局列表

抽屉列表的布局和内容取决于你自己实现的App的需求,但是一般都是以ListView配合Adapter来实现,这里我们用一个String数组来填充抽屉列表,部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MainActivity extends Activity {
private String[] mPlanetTitles;
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViews();
setListener();
}

private void setListener() {
mDrawerList.setAdapter(new ArrayAdapter&lt;&gt;(this, R.layout.drawer_list_item, mPlanetTitles));
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
}

private void findViews() {
mPlanetTitles = getResources().getStringArray(R.array.planets_array);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
}
}

这一步很简单,就是一些初始化抽屉列表,填充数据,设置一下点击事件

阅读全文 »

Android Studio ADB not responding

发表于 2014-12-30 | 分类于 Android

今天想把一个Android Eclipse项目导出到Android Studio中之后,就Google了一下教程,整个导出过程也挺顺利,但是后面还是遇到了一个棘手的问题,我调试用的虚拟机是Genymotion,之前eclipse 和studio都是公用这一个Genymotion虚拟机调试,但是这次在运行从Eclipse导出到Studio的项目的时候,一直提示:

1
2
ADB not responding. If you\'d like to retry
then please manually kill "adb" and click \'Restart\'

刚开始我以为是刚导出的原因,是不是之前Eclipse的adb连着这个Genymotion虚拟机,导致出现这样的错误,于是我就开始尝试:

  • 关闭Genymotion虚拟机,重新在Studio中通过Genymotion的插件启动Genymotion虚拟机,结果是:Not work
  • 重启电脑,依然Not work

我试着Google了一下,发现好像蛮多人遇到这个问题,但是要么就是别人在Stack Overflow提问没人回答,要么就是有人给出答案,无非就是说因为电脑里面有两个sdk目录(一个eclipse的sdk,一个studio的sdk),需要重启一下adb服务,也就是执行下面这两行代码:

1
2
adb kill-server
adb start-server

阅读全文 »

2014年总结

发表于 2014-12-29 | 分类于 Twitter

2014年就要过完了,是时候好好写个总结了,趁着最近这几天休假,好好梳理一下,在2014年底把总结写一下。

  • 工作
    优点:今年六月在入司一年之后顺利转正,也如愿拿到优秀校招生.整体来说这一年工作上提升了很多,待人接物等都有长足进步.技术上对于Android系统层面和Linux脚本这两大方面的提升比较明显一些,毕竟工作接触技术方面太多,Android系统开发啊,Android应用开发,C++,Makefile等技术都有接触,但是工作主要还是偏向Android系统开发,所以Android系统层面上的东西了解的更多一些.
    不足:因为工作职位的原因,对于Android应用的开发做的并不是很多,上半年没有转正的时候还会做一些Android应用项目,但是下半年新的一批校招大学生进入公司之后,Android应用开发就交给他们了,我就主要负责Android系统这一块了,其实自己内心里面还是更喜欢Android应用开发多一些,所以只能在平时没事的时候自己学习,编写一些Android应用的Demo来防止自己对于Android应用生疏了.

    阅读全文 »

echo命令在bash和dash上的区别

发表于 2014-12-23 | 分类于 Linux

今天自己在新服务器上编译的时候突然发现无法生成备份包了,同样的源代码在之前用的服务器上编译就很正常的可以生成备份包,于是觉得很好奇,为什么会这样呢?

根据错误信息提示发现,由于生成的一个ini配置文件每一行的前面出现了无法识别的-e,打开配置文件一看还真的是每行前面都多出来了一个-e,还以为是之前新服务器没有配置好造成的,把编译生成的ini文件删除之后,重新编译一遍发现新生成的ini文件一样还是每行前面都有一个-e,我就很纳闷了,然后我就看了一下MakeFile中生成这个配置文件的这一段代码,基本都是这样的:

1
2
3
4
output_config_file: BUILD_CFG:=${SAVE_FOLDER}/cfg_build.ini
output_config_file:
@echo -e "[CFG_BUILD]" > ${BUILD_CFG}
@echo -e "BOARD = $(BOARD_ID)" >> ${BUILD_CFG}

上面这段代码的内容很简单啊,无非就是在生成的cfg_build.ini中输出一些需要的内容,为什么会在cfg_build.ini的每行前面多处一个-e,实在搞不明白之后就找负责服务器的同事问了一下,他说也不太清楚要问一下这个Makefile脚本的作者,于是又找到了Makefile脚本的作者,他告诉了我一句bash和dash的区别,自己也不太清楚,就Google了一下,慢慢地就搞清楚了,惊叹原来是这样的

阅读全文 »
1…345…7
picksomething

picksomething

专注Android,Java,RN,Python

65 日志
17 分类
148 标签
RSS
© 2018 PICKSOMETHING
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4