git-osc-android客户端之源码分析2

git源码剖析-init-db 我们先找到git代码,然后找到git第一版的代码。 https://github.com/git/git/commit/e83c5163316f89bfbde7d9ab23ca2e25604af290 也就是这个,git clone下来。 我们看到第一次提交的文件比较少。 通过make可以生成7个可执行文件,也正说明了初代的git只实现了以下几个功能。 要研究,我们先研究以下他们怎么... 阅读详情

一、登录界面的布局
在AndroidManifest文件中LoginActivity的配置如下:
登录界面布局的配置
需要配置:
1、android:screenOrientation=”portrait”这是为了让登录界面始终保持为竖屏方向。
2、android:windowSoftInputMode=”adjustUnspecified”这是设置的输入法的一个属性,特点:”adjustUnspecified”(未指定状态:系统会根据界面采取相应的软键盘显示模式),它不指定是否该Activity主窗口调整大小以便留出软键盘的空间,或是否窗口上的内容得到屏幕上的焦点是可见的。系统将自动选择这些模式中的一种主要依赖于窗口的内容是否有任何布局视图能够滚动他们的内容。如果有这样一个视图,这个窗口将调整大小,这样的假设可以使滚动窗口的内容在一个较小的区域中可见。这个是主窗口默认的行为设置。
界面布局如下:
登录界面
登录界面布局代码:
activity.login.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <net.oschina.gitapp.widget.StretchScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <ImageView
                android:layout_width="55dp"
                android:layout_height="55dp"
                android:layout_gravity="center_horizontal"
                android:layout_marginTop="25dp"
                android:src="@drawable/icon_login_logo" />

            <LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:gravity="center"
                android:orientation="vertical"
                android:padding="25dip">

                <AutoCompleteTextView
                    android:id="@+id/et_account"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                        android:drawableLeft="@drawable/icon_login_account"
                    android:drawablePadding="10dip"
                    android:ems="10"
                    android:hint="@string/login_account_hint"
                    android:imeOptions="actionNext"
                    android:inputType="textEmailAddress"
                    android:singleLine="true" />

                <EditText
                    android:id="@+id/et_password"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="20dip"
                                 android:drawableLeft="@drawable/icon_login_pass"
                    android:drawablePadding="10dip"
                    android:ems="10"
                    android:hint="@string/login_password_hint"
                    android:inputType="textPassword"
                    android:singleLine="true" />

                <Button
                    android:id="@+id/bt_login"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="35dip"
                    android:background="@drawable/btn_style_login"
                    android:enabled="false"
                    android:padding="10dip"
                    android:text="@string/login_title"
                    android:textColor="@color/white"
                    android:textSize="@dimen/space_15" />

                <LinearLayout
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="25dip"
                    android:orientation="vertical">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="left"
                        android:text="tips:"
                        android:textColor="@color/gray"
                        android:textSize="12sp" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="15dip"
                        android:text="请使用Git@OSC的push邮箱和密码登录"
                        android:textColor="@color/gray"
                        android:textSize="12sp" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="15dip"
                        android:autoLink="web"
                        android:gravity="left"
                        android:text="注册请前往https://git.oschina.net"
                        android:textColor="@color/gray"
                        android:textSize="12sp" />
                </LinearLayout>
            </LinearLayout>
        </LinearLayout>
    </net.oschina.gitapp.widget.StretchScrollView>
</LinearLayout>

可以看到整个界面是一个线性布局,在线性布局里添加了一个自定义StretchScrollView,可以借鉴的地方有:
1)在用户名和密码输入框中都有一个属性:
android:drawableLeft=”@drawable/icon_login_account”
可以设置出一个小图片来,替代丑陋的文字:用户名、密码,将这些文字改为hint(提示)。

二、用户名和密码的加密、解密,存储和恢复显示
登录流程如下图所示:
这里写图片描述
用户名和密码的加密和解密:
用户名和密码属于敏感型数据,涉及到用户隐私和安全等重要信息,因此要进行加密存储,加密传输,源码中的加密是通过DES算法进行加密的。
加密并存储用户信息的代码:

//保存用户名和密码     AppContext.getInstance().saveAccountInfo(CyptoUtils.encode(Contanst.ACCOUNT_EMAIL, email)
                , CyptoUtils.encode(Contanst.ACCOUNT_PWD, passwd));

解密并回显用户信息的代码:

//解密回显用户名信息
        String account = CyptoUtils.decode(Contanst.ACCOUNT_EMAIL, AppContext.getInstance()
                .getProperty(Contanst.ACCOUNT_EMAIL));
        etAccount.setText(account);
//解密回显用户密码信息
        String pwd = CyptoUtils.decode(Contanst.ACCOUNT_PWD, AppContext.getInstance().getProperty
                (Contanst.ACCOUNT_PWD));
        etPassword.setText(pwd);

具体的加密代码在CyptoUtils.java中。

三、输入法的调用和显示
1、要使用输入法,必须获取输入法的服务:

imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);

2、输入法的隐藏,在输入法中点击了完成之后,进行的响应操作:

 //在输入法里点击了“完成”,则去登录
 if (actionId == EditorInfo.IME_ACTION_DONE) {
     checkLogin();
     //将输入法隐藏
    InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);         imm.hideSoftInputFromWindow(etPassword.getWindowToken(), 0);
    return true;
  }
return false;

总之,在使用输入法之前,需要获取输入法服务!

四、在输入法中点击“完成”和在界面中点击“登录”后,不同的处理方式
在输入法中点击“完成”后登录的代码:

//在输入法里点击了“完成”,则去登录
 if (actionId == EditorInfo.IME_ACTION_DONE) {
     checkLogin();
     //将输入法隐藏
     InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);            imm.hideSoftInputFromWindow(etPassword.getWindowToken(), 0);
    return true;
}

在界面中点击“登录”按钮后登录的代码:

 @Override
 @OnClick(R.id.bt_login)
public void onClick(View v) {       imm.hideSoftInputFromWindow(etPassword.getWindowToken(), 0);
 checkLogin();
 }

两种方式都会完成如下的流程:
1、隐藏输入法;
2、校验输入数据是否合法
3、进行登录

五、登录验证:登录成功、失败的不同提示
如下流程图所示,即登录校验详情流程图:
登录校验流程图

六、用户信息存储包括:
1、用户名、密码的存储;

mAppContext.saveAccountInfo(CyptoUtils.encode(Contanst.ACCOUNT_EMAIL,email),CyptoUtils.encode(Contanst.ACCOUNT_PWD,passwd));

这里调用了AppContext.java中的saveAccountInfo()方法:

/**
* 保存用户的email和pwd
*/
public void saveAccountInfo(String email, String pwd){
      setProperty(ACCOUNT_EMAIL, email);
      setProperty(ACCOUNT_PWD, pwd);
}

这里 setProperty方法调用了AppConfig.getAppConfig()用来进行用户信息的设置,这里使用了单例模式,保证整个程序中只有一份AppConfig配置:

public void setProperty(String key, String value) {
        AppConfig.getAppConfig(this).set(key, value);
    }

接着,set方法获取Properties来进行数据的存储:
public void set(String key,String value)
{
Properties props=get();
props.setProperty(key,value);
setProps(props);
}
接着,调用了setProps()方法:

private void setProps(Properties p) {
        FileOutputStream fos = null;
        try {
            // 把config建在files目录下
            // fos = activity.openFileOutput(APP_CONFIG, Context.MODE_PRIVATE);
            // 把config建在(自定义)app_config的目录下
            File dirConf = mContext.getDir(APP_CONFIG, Context.MODE_PRIVATE);
            File conf = new File(dirConf, APP_CONFIG);
            fos = new FileOutputStream(conf);

            p.store(fos, null);
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (Exception e) {
            }
        }
    }

用户名和密码的存储流程图如下:
这里写图片描述

2、登录成功后,从服务器获取的user信息的存储。
向服务器请求数据是从login(email,passwd)方法发起的,整个过程使用了异步任务来完成的。
1)异步登录

new AsyncTask<Void,Void,Message>()
{
    @override
    protected Messagge doInBackground(Void...params)
    {
        Message msg=mew Message();
        try
        {
            User user=mAppContext.loginVerify(account,passwd);
            msg.what=1;
            msg.obj=user;
        }catch(Exception e)
        {
            ......
        }
        return msg;
    }
}.execute();

2)从异步登录的代码中可以看到,其中调用了mAppContext.loginVerify(account,passwd),从代码中可以看到–通过ApiClient.login(this,account,pwd)请求user信息,如果!=null,就存储用户信息,否则return ;

public User loginVerify(String account,String pwd)throws AppException
{
    User user=ApiClient.login(this,account,pwd);
    if(null != user)
    {
        //保存登录用户的信息
        saveLoginInfo(user);
    }
    return user;
}

3)接下来调用了AppContext中的saveLoginInfo(user)方法:

 /**
 * 保存登录用户的信息
 */
    @SuppressWarnings("serial")
    public void saveLoginInfo(final User user) {
        if (null == user) {
            return;
        }
        // 保存用户的信息
        this.loginUid = StringUtils.toInt(user.getId());
        this.login = true;
        setProperties(new Properties() {
            {
                setProperty(PROP_KEY_UID, String.valueOf(user.getId()));
                setProperty(PROP_KEY_USERNAME, String.valueOf(user.getUsername()));
                setProperty(PROP_KEY_NAME, String.valueOf(user.getName()));
                setProperty(PROP_KEY_BIO, String.valueOf(user.getBio()));// 个人介绍
                setProperty(PROP_KEY_WEIBO, String.valueOf(user.getWeibo()));
                setProperty(PROP_KEY_BLOG, String.valueOf(user.getBlog()));
                setProperty(PROP_KEY_THEME_ID, String.valueOf(user.getTheme_id()));
                setProperty(PROP_KEY_STATE, String.valueOf(user.getState()));
                setProperty(PROP_KEY_CREATED_AT, String.valueOf(user.getCreated_at()));
                setProperty(PROP_KEY_PORTRAIT, String.valueOf(user.getPortrait()));// 个人头像
                setProperty(PROP_KEY_NEWPORTRAIT, String.valueOf(user.getNew_portrait()));// 个人头像
                setProperty(PROP_KEY_IS_ADMIN, String.valueOf(user.isIsAdmin()));
                setProperty(PROP_KEY_CAN_CREATE_GROUP, String.valueOf(user.isCanCreateGroup()));
                setProperty(PROP_KEY_CAN_CREATE_PROJECT, String.valueOf(user.isCanCreateProject()));
                setProperty(PROP_KEY_CAN_CREATE_TEAM, String.valueOf(user.isCanCreateTeam()));
                setProperty(ROP_KEY_FOLLOWERS, String.valueOf(user.getFollow().getFollowers()));
                setProperty(ROP_KEY_STARRED, String.valueOf(user.getFollow().getStarred()));
                setProperty(ROP_KEY_FOLLOWING, String.valueOf(user.getFollow().getFollowing()));
                setProperty(ROP_KEY_WATCHED, String.valueOf(user.getFollow().getWatched()));
            }
        });
    }

4)接着调用了AppContext中的setProperties(ps)方法

 public void setProperties(Properties ps) {
        AppConfig.getAppConfig(this).set(ps);
    }

5)接着调用了AppConfig中的set(ps)方法

public void set(Properties ps) {
        Properties props = get();
        props.putAll(ps);
        setProps(props);
    }

6)接着调用了AppConfig中的setProps(ps)方法

private void setProps(Properties p) {
        FileOutputStream fos = null;
        try {
            // 把config建在files目录下
            // fos = activity.openFileOutput(APP_CONFIG, Context.MODE_PRIVATE);

            // 把config建在(自定义)app_config的目录下
            File dirConf = mContext.getDir(APP_CONFIG, Context.MODE_PRIVATE);
            File conf = new File(dirConf, APP_CONFIG);
            fos = new FileOutputStream(conf);

            p.store(fos, null);
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (Exception e) 
        }
    }
}

用户数据的存储流程图:
用户数据的存储流程图

Android安卓手机git客户端应用APP 一款能在Android安卓手机使用git客户端应用APP,支持创建本地仓库,支持自定义手机本地仓库目录,支持远程仓库克隆到手机储存,支持提交并推送到远程仓库。 立即下载

相关推荐

Android的Git客户端 Agit.zip

Android的Git客户端 Agit ,Agit 是一个开源的 Android 平台上的 Git 客户端软件。 主要功能包括: 克隆...

git-osc-android客户端之源码分析1

一、项目的整体介绍 1、1 项目的目录结构 项目中一共包括有12个包: 1、net.oschina.gitapp:存放了项目中的一些框架类,AppConfig、AppContext、AppManager、WelcomePage等基础类。 2、net.oschina.gitapp.adapter:存放了项目中ListView的数据适配器。 3、net.oschina.gitapp.api:存

c1925363518的博客 594

Android的Git客户端 Agit

Agit是关于Android的Git客户端,包括:克隆远程的 Git 资料库到手机、可离线模式访问完整的历史记录、动画的差异比较、支持 git, http, https 协议,支持 SSH 访问私库、目前还只是只读的客户端,不能提交和push!

git 源码解析(四)commit-tree

commit-tree命令类似于以后的git commit命令,格式 commit-tree <sha1> [-p <sha1>] int main(int argc, char **argv) { unsigned char tree_sha1[20]; get_sha1_hex(argv[1], tree_sha1); for (i = 2...

chenwh_cn的博客 996

OpenHarmony-AI调研

基于OpenHarmony-5.0.0版本以及laval社区等网络资料调研当前OH上的AI发展情况。

qqq1112345的博客 1367

如何设计QR反激变换器

摘要:本文分析了基于GaN技术的PD适配器市场现状,重点探讨了准谐振反激变换器(QR Flyback)的工作原理及设计方法。文章首先介绍了反激变换器的三种工作模式(CCM、DCM、CRM/QR)及其特点,指出QR模式通过谷底开通技术能显著降低开关损耗。随后以65W适配器为例,详细阐述了变压器参数设计流程,包括匝比确定、原边电感计算和绕组匝数设计等关键步骤。文章强调QR反激变换器凭借高效率、小体积和低成本优势,已成为20W-65W PD适配器的主流拓扑方案,配合GaN技术进一步提升了功率密度和便携性。

m0_62868254的博客 1496

git 源码解析(一)init-db

第一次用git的时候就非常好奇,不知道这么多命令背后功能是怎么实现的。 后来在github上找到源码,回退到第一个初始节点,发现只有几个基本的.c文件,虽然不是完成的功能,但是一些基本的实现原理和逻辑还是挺清楚的。 比如对源文件内容的压缩、取hash值、压缩数据存入hash值命名的文件、tree、commit作用等等。 现在对这几个.c文件代码做简单梳理,主要用于了解功能实现的逻辑,如有错误...

chenwh_cn的博客 1082

TC3xx芯片SPI多从机通信避坑指南:AUTOSAR框架下的DMA配置与片选切换

本文针对英飞凌TC3xx芯片在AUTOSAR框架下实现SPI多从机通信的实战难题,提供了详尽的避坑指南。重点解析了如何利用硬件支持的16路片选(SLSO)灵活切换,在AUTOSAR中正确配置不同时序的从设备,并深入探讨了异步模式下DMA的启用条件与关键配置细节,帮助开发者规避通信失败风险,提升系统可靠性。

nn3456的博客 153

Git 最原始的开源版本代码解析

Git 的最初版本虽然简单,但已经奠定了其核心设计理念。通过对象数据库和索引机制,Git 实现了高效的版本控制功能。这个版本的代码虽然只有约 1000 行,但它展示了 Linus Torvalds 的天才设计和对简洁性的追求。

weixin_44727517的博客 876

AutoSAR开发实战:如何用Matlab/Simulink从零搭建SWC组件(附完整代码示例)

本文详细介绍了使用Matlab/Simulink从零开发AutoSAR SWC组件的全流程,包括环境配置、ARXML文件导入、算法模型开发、代码生成与集成验证等关键步骤。通过实战案例和完整代码示例,帮助开发者掌握AutoSAR标准下的软件开发技术,提升汽车电子系统的开发效率。

weixin_29093017的博客 256

Ubuntu 24.04 搭建 Squid 代理服务器终极指南:从入门到 HTTPS 缓存AI模型

本文详细指导在Ubuntu 24.04系统部署Squid代理服务器,实现AI大模型文件的高效缓存。重点内容包括:1) 安装squid及必备的squid-openssl扩展包;2) 配置HTTPS拦截(SSL-Bumping)功能,创建CA证书和SSL数据库;3) 优化缓存策略,设置4TB存储空间并强制缓存常见大文件格式;4) 结合华为防火墙实现透明代理。文章特别强调实际部署中的关键注意事项和常见错误解决方案,如证书路径配置、权限设置等问题,帮助用户快速搭建企业级缓存代理服务。

zlt315的博客 1920

Git 源代码分析

Git介绍 Git --- The stupid content tracker, 傻瓜内容跟踪器。Linus 是这样给我们介绍 Git 的。 Git 是用于 Linux 内核开发的版本控制工具。与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式

3660

Coze 打通飞书多维表格,实现数据增删改查操作实战详解

基于Coze 打通飞书多维表实现数据录入系统实战详解

congge 5779

git-osc-android客户端之源码分析3

一、侧滑菜单栏 工作流程图: 当菜单栏显示时,点击左上角图标,隐藏菜单栏;菜单栏隐藏时,显示菜单栏。 当点击菜单栏的某一条目后,跳转到对应的界面。 1、点击登录后,进入登录界面; 2、发现,进入发现界面; 3、我的,进入我的界面; 4、语言,进入我的语言界面; 5、摇一摇,进入摇一摇界面; 6、设置,进入设置界面; 7、反馈,进入反馈界面; 界面之间的跳转则是封装在了UIH

c1925363518的博客 474

深入详细分析Skyworks 78113 PAMiD射频方案图

是一种高集成度的射频前端模块,它结合了功率放大器(PA)、双工器(Duplexer)、滤波器和开关等多个组件,主要用于4G LTE移动通信系统中的射频前端链路。是一款典型的PAMiD模块,通过将多个频段的发射(TX)和接收(RX)路径集成在一个模块中,大大减少了传统射频设计中的分立器件,同时提高了信号链路的效率和可靠性。模块化设计的优势PCB空间优化:高集成度设计大幅减小了射频电路的尺寸,适用于紧凑型移动设备。减少设计复杂度:模块化设计减少了器件间的匹配与调试工作,加速产品开发周期。性能稳定性。

weixin_43199439的博客 1480

产品需求从场景,初始需求IR,产品需求 OR,设计需求DR,设计规格DS的具体案例说明,以扫地机产品为例

摘要: 以扫地机器人"宠物粪便避障"功能为例,说明从用户场景到产品落地的完整需求转化路径。初始需求(IR)源于养宠家庭痛点,要求100%避障并自动告警;产品需求(OR)转化为≥98%识别准确率等量化指标;设计需求(DR)拆解为感知、决策、执行三大技术模块;最终形成传感器选型(如索尼IMX678)、芯片规格(高通QCS6490)等设计规格(DS)。通过实验室模拟和用户测试验证,该功能使产品溢价30%,养宠家庭购买占比达52%,实现技术与商业价值的统一。

AIoT深度探索者 | 嵌入式系统架构师 深耕物联网(IoT)与人工智能(AI)融合技术,聚焦​​边缘计算、图像开发、低功耗通信协议​​,致力于智能硬件,关注我获取最新技术文章信息与海量资源~ 763

Git源码分析

Git源码分析

Ternence_zq的博客 4015

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

git 源码解析(五)cat-file

int main(int argc, char **argv) { unsigned char sha1[20]; if (argc != 3 || get_sha1_hex(argv[2], sha1)) usage("cat-file: cat-file [-t | tagname(blob/tree/commit)] <sha1>"); ...

chenwh_cn的博客 1235

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

git@osc使用教程

Git初体验 http://my.oschina.net/dxqr/blog/134811 网友整理的git@osc教程 http://git.oschina.net/oschina/git-osc/wikis/help#toc_19 git@osc工作流程 http://git.oschina.net/oschina/git-osc/wikis/help

wzk456的专栏 3356

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Git@OSC Push问题

GitOSC

jmppok的专栏 3456

Git@OSC push 不用每次输入用户名和密码的方法

每次git push时,都要需要输入用户名和密码,实在是太烦。在网上找了很多资料,还是下面这个靠谱: http://www.cnblogs.com/dudu/archive/2011/07/06/git_save_username_password.html http://blog.youkuaiyun.com/dai_zhenliang/article/details/18653377 其实以

坤的专栏 3953

在Android上使用Git –免费工具和指南

当今最大的挑战以及对自由职业者和远程团队的需求,是“移动”。 尽管移动代码编辑器可以帮助程序员在旅途中进行开发,但是如果没有对Git之类的版本控制系统的移动支持,开发人员几乎就不会使用此类IDE。 但是, 有一些应用程序可以在Android平台上设置和使用Git存储库 。 适用于Android的Git客户端,服务器和IDE帮助编码人员随时随地进行编码,并将其推送到 GitHub ,BitB...

cune1359的博客 1万+

【亲测免费】 SGit Android Git 客户端使用教程

SGit 是一个专为 Android 平台设计的 Git 客户端,允许用户在移动设备上管理和操作 Git 仓库。该项目最初由 sheimi 开发,但由于维护资源有限,后续开发已转移到 MGit 项目。SGit 支持多种 Git 操作,包括创建本地仓库、克隆远程仓库、拉取和推送代码等。 ## 2. 项目快速启动 ### 2.1 克隆远程仓库 1. 打开 SGit 应用。 2. 点击右上角的 `...

gitblog_00768的博客 1656

MGit安卓Git客户端终极指南:从安装到高级使用完整教程

想要在Android设备上轻松管理Git仓库吗?MGit作为一款功能强大的Android Git客户端,为移动端开发者提供了完整的版本控制解决方案。本教程将为您详细介绍MGit的安装步骤、核心功能和使用技巧,帮助您实现手机Git仓库的高效管理。 ## 📱 MGit安装指南:快速上手Android代码版本控制 MGit支持通过多种渠道安装,您可以选择最适合您的方式: **Google Pla

gitblog_00272的博客 1301

android版 git客户端下载,Pocket Git 是 Android 手机上一个 Git 客户端工具

Pocket Git 是 Android 手机上一个 Git 客户端工具,功能包括:支持 Git 的各种操作图形化的提交日志查看功能内置 diff 文件比较支持 SSH 密钥或者用户名密码方式登录可以自动捕获 Github 或者 Bitbucket 的 URL, 只需使用浏览器分享功能给 Pocket Git 就可以了支持在外部编辑器打开文件Pocket Git is a powerful sta...

weixin_42515796的博客 3694

Github客户端下载(安卓系统)

Github安卓客户端下载

m0_55316295的博客 1万+
上一篇: git-osc-android客户端之源码分析1
下一篇: git-osc-android客户端之源码分析3
c1925363518
博客等级 码龄11年 4粉丝 14原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值