Hello,
I just started with developing in csharp for my mobile phone. The first project i'm working on is creating a GPS tracker program that will safe the current GPS location to a text file. This file i'll load to a website from where people can download an google earth file (i prefer php programming )
But to get to the point. The program worked fine untill i added some extra functionality (SystemIdleTimerReset and the date in the name of the datafile)
After adding this the program starts in the emulator, however after 30 seconds (when the trigger start for the first time) it comes with the exception (at the red comment):
Exception was unhandeld
The method or operation is not implemented
My code (i added everything from the main form, since i don't know where the error is)
Code:
using System;
using System.Collections;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Microsoft.WindowsMobile.Samples.Location;
namespace GPS_Tracker
{
public partial class mainform : Form
{
GpsDeviceState device = null;
GpsPosition position = null;
Gps gps = new Gps();
int iTicker = 0;
int iCounter = 0;
string fileName;
private EventHandler updateDataHandler;
private EventHandler timerTicker;
DateTime currentTime = DateTime.Now;
public mainform()
{
InitializeComponent();
}
private void menuExit_Click(object sender, EventArgs e)
{
if (gps.Opened)
{
gps.Close();
}
Close();
}
private void menuStart_Click(object sender, EventArgs e)
{
if (!gps.Opened)
{
gps.Open();
}
menuStart.Enabled = false;
menuStop.Enabled = true;
}
private void menuStop_Click(object sender, EventArgs e)
{
if (gps.Opened)
{
gps.Close();
}
menuStart.Enabled = true;
menuStop.Enabled = false;
}
private void mainform_Load(object sender, EventArgs e)
{
timerTicker = new EventHandler(timerCheck);
updateDataHandler = new EventHandler(UpdateData);
gps.DeviceStateChanged += new DeviceStateChangedEventHandler(gps_DeviceStateChanged);
gps.LocationChanged += new LocationChangedEventHandler(gps_LocationChanged);
Status.Text = "";
}
protected void gps_LocationChanged(object sender, LocationChangedEventArgs args)
{
position = args.Position;
// call the UpdateData method via the updateDataHandler so that we
// update the UI on the UI thread
Invoke(timerTicker);
}
void gps_DeviceStateChanged(object sender, DeviceStateChangedEventArgs args)
{
device = args.DeviceState;
// call the UpdateData method via the updateDataHandler so that we
// update the UI on the UI thread
Invoke(timerTicker);[COLOR="Red"]// place where error comes[/COLOR]
}
void timerCheck(object sender, System.EventArgs args)
{
Invoke(updateDataHandler);
}
void UpdateData(object sender, System.EventArgs args)
{
if (iTicker == 1)
{
SystemIdleTimerReset();
if (gps.Opened)
{
string str = "";
if (device != null)
{
str = device.FriendlyName + " " + device.ServiceState + ", " + device.DeviceState + "\n";
}
if (position != null)
{
iCounter++;
if (position.SeaLevelAltitudeValid &&
position.EllipsoidAltitudeValid &&
position.SpeedValid &&
position.LatitudeValid &&
position.LongitudeValid &&
position.SatellitesInSolutionValid &&
position.SatellitesInViewValid &&
position.SatelliteCountValid &&
position.TimeValid)
{
fileName = "data_" + currentTime.ToString("yyyyMMdd") + ".txt";
StreamWriter output;
output = File.AppendText(fileName);
output.WriteLine(position.SeaLevelAltitude + ";" +
position.EllipsoidAltitude + ";" +
position.SpeedKmh + ";" +
position.Longitude + ";" +
position.Latitude + ";" +
position.GetSatellitesInSolution().Length + "/" +
position.GetSatellitesInView().Length + " (" +
position.SatelliteCount + ";" +
position.Time.ToString());
output.Close();
str += "Saving data to File \nEntry: " + iCounter + "\n";
str += "Current speed: " + position.SpeedKmh + "\n";
str += "Current height: " + position.SeaLevelAltitude + "\n";
}
}
Status.Text = str;
}
iTicker = 0;
}
}
private void SystemIdleTimerReset()
{
throw new Exception("The method or operation is not implemented.");
}
private void Ticker_Tick(object sender, EventArgs e)
{
iTicker = 1;
}
}
}
the error details:
Error details:
System.Exception was unhandled
Message="The method or operation is not implemented."
StackTrace:
at GPS_Tracker.mainform.SystemIdleTimerReset()
at GPS_Tracker.mainform.UpdateData()
at TASK.Invoke()
at System.Windows.Forms.Control._InvokeAll()
at System.Windows.Forms.Control.InvokeHelper()
at System.Windows.Forms.Control.Invoke()
at GPS_Tracker.mainform.timerCheck()
at TASK.Invoke()
at System.Windows.Forms.Control._InvokeAll()
at System.Windows.Forms.Control.WnProc()
at System.Windows.Forms.ContainerControl.WnProc()
at System.Windows.Forms.Form.WnProc()
at System.Windows.Forms.Control._InternalWnProc()
at Microsoft.AGL.Forms.EVL.EnterMainLoop()
at System.Windows.Forms.Application.Run()
at GPS_Tracker.Program.Main()
Click to expand...
Click to collapse
I hope anybody can help me.
Thanks in advance
StruiS
I removed (commented) the SystemIdleTimerReset function and it works again.
Can someone help me implement this function?
Thanks
Hello,
I've been trying to do some android stuff on java for some time now, and i've come across a problem here: i can't get the app to execute linux stuff, as there is no system() method like on other platforms... so i searched some code and found this:
Code:
protected void system(String[] Commands){
e Process process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
DataInputStream osRes = new DataInputStream(process.getInputStream());
Vector<String> res = new Vector<String>();
for (String single : Commands) {
e os.writeBytes(single + "\n");
e os.flush();
e res.add(osRes.readLine());
// Log.e("CMDs", osRes.readLine());
}
e os.writeBytes("exit\n");
e os.flush();
process.waitFor();
}
However that won't work because of some errors i have in the marked lines:
"Unhandled exception type IOException"
and the process.waitFor(); line also gives me an error:
"Unhandled exception type InterruptedException"
Any ideas?
You need to add a try/catch block around that code which catches the IO exception and the interrupted exception.
deleted
So, first of all thanks to both of you it appears to be working now... i tried in on the emulator, and of course "su" didn't work there (broken pipe), so i replaced it by "sh", however this didn't seem to work well too. the application just locked up with a warning in android.... strange...
edit: tried using /system/bin/sh, didn't work, locked up again
What version of Android in the emulator? I've done it with 1.5 through 2.2 in the emulator, just by using "sh".
could you post the code you used please? would be AWESOME!
i'm trying to get this working on 2.1
Sure, I can post some more details later, but for now just the code.
Include the file in your project and use with:
Code:
ShellCommand cmd = new ShellCommand();
CommandResult r = cmd.sh.runWaitFor("ls -l");
if (!r.success()) {
Log.v(TAG, "Error " + r.stderr);
} else {
Log.v(TAG, "Success! " + r.stdout);
}
If you want su you can either use cmd.shOrSu().runWaitFor("..."); which will try su (by running "id", it just tests the status code but it's a nice entry in logcat for debugging) and fallback to sh. Or you can use cmd.su.runWaitFor("...");
Also at
teslacoilsw.com/files/ShellCommand.java
Code:
package com.teslacoilsw.quicksshd;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import android.util.Log;
public class ShellCommand {
private static final String TAG = "ShellCommand.java";
private Boolean can_su;
public SH sh;
public SH su;
public ShellCommand() {
sh = new SH("sh");
su = new SH("su");
}
public boolean canSU() {
return canSU(false);
}
public boolean canSU(boolean force_check) {
if (can_su == null || force_check) {
CommandResult r = su.runWaitFor("id");
StringBuilder out = new StringBuilder();
if (r.stdout != null)
out.append(r.stdout).append(" ; ");
if (r.stderr != null)
out.append(r.stderr);
Log.v(TAG, "canSU() su[" + r.exit_value + "]: " + out);
can_su = r.success();
}
return can_su;
}
public SH suOrSH() {
return canSU() ? su : sh;
}
public class CommandResult {
public final String stdout;
public final String stderr;
public final Integer exit_value;
CommandResult(Integer exit_value_in, String stdout_in, String stderr_in)
{
exit_value = exit_value_in;
stdout = stdout_in;
stderr = stderr_in;
}
CommandResult(Integer exit_value_in) {
this(exit_value_in, null, null);
}
public boolean success() {
return exit_value != null && exit_value == 0;
}
}
public class SH {
private String SHELL = "sh";
public SH(String SHELL_in) {
SHELL = SHELL_in;
}
public Process run(String s) {
Process process = null;
try {
process = Runtime.getRuntime().exec(SHELL);
DataOutputStream toProcess = new DataOutputStream(process.getOutputStream());
toProcess.writeBytes("exec " + s + "\n");
toProcess.flush();
} catch(Exception e) {
Log.e(QuickSSHD.TAG, "Exception while trying to run: '" + s + "' " + e.getMessage());
process = null;
}
return process;
}
private String getStreamLines(InputStream is) {
String out = null;
StringBuffer buffer = null;
DataInputStream dis = new DataInputStream(is);
try {
if (dis.available() > 0) {
buffer = new StringBuffer(dis.readLine());
while(dis.available() > 0)
buffer.append("\n").append(dis.readLine());
}
dis.close();
} catch (Exception ex) {
Log.e(TAG, ex.getMessage());
}
if (buffer != null)
out = buffer.toString();
return out;
}
public CommandResult runWaitFor(String s) {
Process process = run(s);
Integer exit_value = null;
String stdout = null;
String stderr = null;
if (process != null) {
try {
exit_value = process.waitFor();
stdout = getStreamLines(process.getInputStream());
stderr = getStreamLines(process.getErrorStream());
} catch(InterruptedException e) {
Log.e(TAG, "runWaitFor " + e.toString());
} catch(NullPointerException e) {
Log.e(TAG, "runWaitFor " + e.toString());
}
}
return new CommandResult(exit_value, stdout, stderr);
}
}
}
Thanks kevin The code you are using there is awesome Looking good so far, however it keeps returning permission denied... is it some setting in the android manifest?
Actually "Permission denied" often also means "no such file or directory" on android :-/ . It's very frustrating.
Try running something simple to start with like:
cmd.sh.runWaitFor("echo foo");
[email protected] said:
Actually "Permission denied" often also means "no such file or directory" on android :-/ . It's very frustrating.
Try running something simple to start with like:
cmd.sh.runWaitFor("echo foo");
Click to expand...
Click to collapse
yep, i tried running echo as i was confused by the "permission denied" although i had already set write permissions for the sdcard... didn't work, for some odd reason
I have a simple app that I'm trying to develop. I have written Android apps for work but this would be the first app with root access, and it is not for work. Here is my onCreate code.
Code:
setContentView(R.layout.main);
/**
* Set up the spinner for the buffer size
*/
spinner = (Spinner) findViewById(R.id.spKBSelect);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this, R.array.KBs, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
txtCurVal = (TextView) findViewById(R.id.txtCurrentV);
try {
txtCurVal.setText(getString(R.string.cursetvalue) + " " + getCurrentValue() + " KB");
p = Runtime.getRuntime().exec("su");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
This code works fine. I get root access from Superuser. Now when I click the button in the following code it works. The only thing is that when I try to refresh my txtCurVal.setText() after the update I get the same value. I have checked and the update to the file did go through. So what I think is that the su process and/or commands dealing with the su have not finished when I read the value again. After searching online most people were saying that I needed to add a p.waitfor(); so that the terminal could finish. This is where the problems occurs it gets to the point and the app just stops. After a few second Android says that the app is non responsive and wants to force close. Here is the button click code.
Code:
public void onClick(View v) throws Exception {
switch (v.getId()) {
case R.id.btnAbout:
Toast.makeText(this, "Created by: Ben Murphy (Smurph82)\nCreated on: 2011_0618", Toast.LENGTH_LONG).show();
break;
case R.id.btnSave:
DataOutputStream os = new DataOutputStream(p.getOutputStream());
DataInputStream osRes = new DataInputStream(p.getInputStream());
//backupFile(getString(R.string.rakpath));
os.writeBytes("busybox cp " + getString(R.string.rakpath) + " " + sdcard + "/.sdspeedshifter\n");
os.flush();
os.writeBytes("busybox mv -f " + sdcard + "/.sdspeedshifter/read_ahead_kb " + " " + sdcard + "/.sdspeedshifter/read_ahead_kb_bak\n");
os.flush();
//writeNewValue(spinner.getSelectedItem().toString(), getString(R.string.rakpath));
os.writeBytes("echo \"" + spinner.getSelectedItem().toString() + "\" > " + getString(R.string.rakpath) + "\n");
os.flush();
os.writeBytes("exit\n");
os.flush();
[B]p.waitFor();[/B]
txtCurVal.setText(getString(R.string.cursetvalue) + " " + getCurrentValue() + " KB");
break;
default:
break;
}
}
If I take the p.waitfor(); out everything runs but the value is not displayed correctly. This is just an app for me to learn how to use su access. As you can tell all this does is increase the value in the read_ahead_kb file that helps with sd card speed. Any help of advice would be great. Thanks.
I don't see "p" every declared, so there's no way to know, given what you've posted, whether it is in-scope for both "setContentView" and "onClick" so that may be why you're app is hanging, but I don't think this is the way to go about it. Running a bare "su" command in exec is going to spawn off a new command shell with root privileges, but without passing any arguments, the shell is just going to sit there assuming it is a login shell, waiting for the user to type commands.
Generally, if you need root level control over some resource, you just do it once for the time it is needed and then drop the privilege once the work is done. I typically write a small C code executable that does the privileged stuff, then call is as a command line argument for the su command with the "-c" argument:
Code:
Runtime.getRuntime().exec("su -c mycmd");
Now, mycmd runs, does what it needs to do, then exits returning full control to the app (no hang).
You need to use JNI or another toolchain to write command-line exe's for the device.
Here is the whole class.
Code:
public class SDSpeedShifterActivity extends Activity {
private final static File sdcard = Environment.getExternalStorageDirectory();
private static Process p = null;
private static TextView txtCurVal = null;
private static Spinner spinner = null;
private static StringBuilder sb = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/**
* Set up the spinner for the buffer size
*/
spinner = (Spinner) findViewById(R.id.spKBSelect);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this, R.array.KBs, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
txtCurVal = (TextView) findViewById(R.id.txtCurrentV);
try {
txtCurVal.setText(getString(R.string.cursetvalue) + " " + getCurrentValue() + " KB");
p = Runtime.getRuntime().exec("su");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* This handles all of the button clicks.
* @param v The view (Button) that was clicked.
* @throws Exception If something goes wrong.
*/
public void onClick(View v) throws Exception {
switch (v.getId()) {
case R.id.btnAbout:
Toast.makeText(this, "Created by: Ben Murphy (Smurph82)\nCreated on: 2011_0618", Toast.LENGTH_LONG).show();
break;
case R.id.btnSave:
DataOutputStream os = new DataOutputStream(p.getOutputStream());
DataInputStream osRes = new DataInputStream(p.getInputStream());
os.writeBytes("busybox cp " + getString(R.string.rakpath) + " " + sdcard + "/.sdspeedshifter\n");
os.flush();
os.writeBytes("busybox mv -f " + sdcard + "/.sdspeedshifter/read_ahead_kb " + " " + sdcard + "/.sdspeedshifter/read_ahead_kb_bak\n");
os.flush();
//writeNewValue(spinner.getSelectedItem().toString(), getString(R.string.rakpath));
os.writeBytes("echo \"" + spinner.getSelectedItem().toString() + "\" > " + getString(R.string.rakpath) + "\n");
os.flush();
os.writeBytes("exit\n");
os.flush();
//p.waitFor();
txtCurVal.setText(getString(R.string.cursetvalue) + " " + getCurrentValue() + " KB");
break;
default:
break;
}
}
/**
* Show the current read_ahead_kb value from the file.
* @param v The view (Textview) that needs to be changed.
* @throws IOException
*/
private final String getCurrentValue() throws IOException{
File rak = new File(getString(R.string.rakpath));
BufferedReader br = new BufferedReader(new FileReader(rak));
String line, vm = "";
while ((line = br.readLine()) != null) {
vm = line;
break;
}
return vm;
}
Thanks for the help.
So I assume it is the "busybox mv..." and/or "busybox cp ..." commands that need root permissions? Just change them to:
Code:
"su -c 'busybox mv ...'"
Search z4root source code in Google and have a good look at VirtualTerminal(in source code) and how to use it.
maybe you want to check out this one:
http://code.google.com/p/roottools/
I'm an experienced developer but new to Android development. I have an app that runs some native binaries, and I provide a status indicator to show when the native process is running and when it's not. Currently I poll the device to figure this out, using the ActivityManager API to determine if specific processes are running or not.
I'm hoping there is some way to register a listener on process state changes, so I can get notified when my process starts or stops. I looked through the API, and there doesn't seem to be such a thing. Does anyone know how I can keep track of process start and stop other than polling via ActivityManager?
MidnightJava said:
I'm an experienced developer but new to Android development. I have an app that runs some native binaries, and I provide a status indicator to show when the native process is running and when it's not. Currently I poll the device to figure this out, using the ActivityManager API to determine if specific processes are running or not.
I'm hoping there is some way to register a listener on process state changes, so I can get notified when my process starts or stops. I looked through the API, and there doesn't seem to be such a thing. Does anyone know how I can keep track of process start and stop other than polling via ActivityManager?
Click to expand...
Click to collapse
Afaik there's no way to accomplish that other than your way or being system/root app. See this similar question here for reference.
Can you show how you start the process?
EmptinessFiller said:
Can you show how you start the process?
Click to expand...
Click to collapse
Sure. Here's the class that manages starting, stopping, and statusing (running or not) the binary executable. In this case, it's the omniNames service of the omni ORB (CORBA broker).
Code:
public class RHManager {
private TimerTask task = new TimerTask() {
@Override
public void run() {
if (RHManager.this.listener != null) {
listener.running(isOmniNamesRunning());
}
}
};
private IStatusListener listener;
public RHManager() {
}
public void startOmniNames() {
final Exec exec = new Exec();
final String[] args = new String[]
{RhMgrConstants.INSTALL_LOCATION_OMNI_NAMES_SCRIPTS + "/" + RhMgrConstants.OMNI_NAMES_SCRIPT_FILE,
"start"};
final String[] env = new String[] {"LD_LIBRARY_PATH=/sdcard/data/com.axiosengineering.rhmanager/omniORB/lib"};
Thread t = new Thread() {
public void run() {
try {
int res = exec.doExec(args, env);
logMsg("omniNames start return code " + res);
} catch (IOException e) {
logMsg("Failed to start omniNames");
e.printStackTrace();
}
String std = exec.getOutResult();
logMsg("omniNames start: std out==> " + std );
String err = exec.getErrResult();
logMsg("omniNames start: err out==> " + err );
};
};
t.start();
logMsg("omniNames started");
}
private boolean isOmniNamesRunning() {
String pid_s = getOmniNamesPid();
Integer pid = null;
if (pid_s != null) {
try {
pid = Integer.parseInt(pid_s);
} catch (NumberFormatException e) {
return false;
}
}
if (pid != null) {
RunningAppProcessInfo activityMgr = new ActivityManager.RunningAppProcessInfo("omniNames", pid, null);
return activityMgr.processName != null ;
}
return false;
}
public void stopOmniNames() {
String pid = getOmniNamesPid();
android.os.Process.killProcess(Integer.parseInt(pid));
android.os.Process.sendSignal(Integer.parseInt(pid), android.os.Process.SIGNAL_KILL);
}
private String getOmniNamesPid() {
Exec exec = new Exec();
final String[] args = new String[]
{RhMgrConstants.INSTALL_LOCATION_OMNI_NAMES_SCRIPTS + "/" + RhMgrConstants.OMNI_NAMES_SCRIPT_FILE,
"pid"};
String pid = "";
try {
int res = exec.doExec(args, null);
logMsg("oniNames pid return code: " + res);
} catch (IOException e) {
logMsg("Failed to start omniNames");
e.printStackTrace();
return pid;
}
String std = exec.getOutResult();
logMsg("omniNames pid: std out ==> " + std);
String err = exec.getErrResult();
logMsg("omniNames pid: err out ==> " + err);
String[] parts = std.split("\\s+");
if (parts.length >= 2) {
pid = parts[1];
}
return pid;
}
//monitor omniNames status and report status periodically to an IStatusListener
public void startMonitorProcess(IStatusListener listener, String string) {
this.listener = listener;
Timer t = new Timer();
t.schedule(task, 0, 1000);
}
private void logMsg(String msg) {
if (RhMgrConstants.DEBUG) {
System.err.println(msg);
}
}
}
Here's the Exec class that handles invocation of Runtime#exec(), consumes std and err out, and reports those and process return status to the caller.
Code:
public class Exec {
private String outResult;
private String errResult;
private Process process;
private boolean failed = false;
StreamReader outReader;
StreamReader errReader;
public int doExec(String[] cmd, String[] envp) throws IOException{
Timer t = null;
try {
process = Runtime.getRuntime().exec(cmd, envp);
outReader = new StreamReader(process.getInputStream());
outReader.setPriority(10);
errReader = new StreamReader(process.getErrorStream());
outReader.start();
errReader.start();
t = new Timer();
t.schedule(task, 10000);
int status = process.waitFor();
outReader.join();
errReader.join();
StringWriter outWriter = outReader.getResult();
outResult = outWriter.toString();
outWriter.close();
StringWriter errWriter = errReader.getResult();
errResult = errWriter.toString();
errWriter.close();
return (failed ? -1: status);
} catch (InterruptedException e) {
return -1;
} finally {
if (t != null) {
t.cancel();
}
}
}
public int doExec(String[] cmd) throws IOException{
return doExec(cmd, null);
}
public String getOutResult(){
return outResult;
}
public String getErrResult(){
return errResult;
}
private static class StreamReader extends Thread {
private InputStream is;
private StringWriter sw;
StreamReader(InputStream is) {
this.is = is;
sw = new StringWriter(30000);
}
public void run() {
try {
int c;
while ((c = is.read()) != -1){
sw.write(c);
}
}
catch (IOException e) { ; }
}
StringWriter getResult() {
try {
is.close();
} catch (IOException e) {
System.err.println("Unable to close input stream in StreamReader");
}
return sw;
}
}
private TimerTask task = new TimerTask() {
@Override
public void run() {
failed = true;
process.destroy();
}
};
}
Here's the script that startOminNames() invokes. It's the shell script installed with omniORB with functions other than start and get_pid removed, since those are handled by Android classes. You can invoke any executable in place of the script, or wrap your executable in a script.
Code:
#
# omniNames init file for starting up the OMNI Naming service
#
# chkconfig: - 20 80
# description: Starts and stops the OMNI Naming service
#
exec="/sdcard/data/com.axiosengineering.rhmanager/omniORB/bin/omniNames"
prog="omniNames"
logdir="/sdcard/data/com.axiosengineering.rhmanager/omniORB/logs"
logfile="/sdcard/data/com.axiosengineering.rhmanager/omniORB/logs/omninames-localhost.err.log"
options=" -start -always -logdir $logdir -errlog $logfile"
start() {
#[ -x $exec ] || exit 5
echo -n $"Starting $prog: "
$exec $options
}
get_pid() {
ps | grep omniNames
}
case "$1" in
start)
start && exit 0
$1
;;
pid)
get_pid
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
exit 2
esac
exit $?
And here's the IStatusListener interface
Code:
public interface IStatusListener {
public void running(boolean running);
}
Runtime.exec() has some pitfalls. See this helpful Runtime.exec tutorial for a nice explanation.
And you may also want to check out this post on loading native binaries in Android.
I am new to Java and I am getting kind of stuck by trying to loop through my JSON. I am retrieving a JSON object where I want to loop through.
My JSON looks as follow:
Code:
{"message":{"2":[{"uid":"2","title":"","message":"Test1","success":1,"created_at":null,"updated_at":null}],"3":[{"uid":"3","title":"","message":"Test2 !","success":1,"created_at":null,"updated_at":null}],"4":[{"uid":"4","title":"Bla","message":"Test3!","success":1,"created_at":null,"updated_at":null}]}}
I tried a loop like this:
Code:
for(int i = 0; i<json.names().length(); i++){
try {
Log.v("TEST", "key = " + json.names().getString(i) + " value = " + json.get(json.names().getString(i)));
} catch (JSONException e) {
e.printStackTrace();
}
}
But this will only target "message" which includes the whole JSON "string". I want to loop through each message and retrieving the value of uid 1, uid 2 etc. How can I achieve this?
Thanks in advance.
Here is how I'm doing it:
Code:
private static final String AllNewsItemsURL = "some_url_here.php";
private static final String TAG_SUCCESS = "success";
private static final String NEWS = "news";
private static final String TITLE = "title";
private static final String STORY = "story";
private final JSONParser jParser = new JSONParser();
private JSONArray newsItems = null;
..... / code snipped / ....
try {
JSONObject json = jParser.makeHttpRequest(AllNewsItemsURL, params);
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
newsItems = json.getJSONArray(NEWS);
for (int i = 0; i < newsItems.length(); i++) {
JSONObject obj = newsItems.getJSONObject(i);
Integer id = i + 1;
String title = obj.getString(TITLE);
String story = obj.getString(STORY);
}
} else {
Log.e("JSON Response", "success == 0");
}
} catch (Exception e) {
e.printStackTrace();
}
Thanks for your reply. I tried it but getting this exception:
Code:
org.json.JSONException: Value {"2":[{"uid":"2","title":"","message":"Test1","success":1,"created_at":null,"updated_at":null}],"3":[{"uid":"3","title":"","message":"Test2","success":1,"created_at":null,"updated_at":null}],"4":[{"uid":"4","title":"Bla","message":"Test3","success":1,"created_at":null,"updated_at":null}]} at messages of type org.json.JSONObject cannot be converted to JSONArray
CodeMonkeyy said:
Thanks for your reply. I tried it but getting this exception:
Code:
org.json.JSONException: Value {"2":[{"uid":"2","title":"","message":"Test1","success":1,"created_at":null,"updated_at":null}],"3":[{"uid":"3","title":"","message":"Test2","success":1,"created_at":null,"updated_at":null}],"4":[{"uid":"4","title":"Bla","message":"Test3","success":1,"created_at":null,"updated_at":null}]} at messages of type org.json.JSONObject cannot be converted to JSONArray
Click to expand...
Click to collapse
You might want to try the matching JSONParser I have for it, sorry forgot to include it:
https://github.com/JonnyXDA/WGSB/bl...om/jonny/wgsb/material/parser/JSONParser.java
Also noting that your entire JSON Array is called "message" but you also have a parameter called "message" - maybe rename the Array to "messages"?
As for the code you should have something like:
Code:
private static final String AllNewsItemsURL = "some_url_here.php";
private static final String TAG_SUCCESS = "success";
private static final String MESSAGES = "messages";
private static final String TITLE = "title";
private static final String MESSAGE = "message";
private final JSONParser jParser = new JSONParser();
private JSONArray messageItems = null;
..... / code snipped / ....
try {
JSONObject json = jParser.makeHttpRequest(AllNewsItemsURL, params);
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
messageItems = json.getJSONArray(MESSAGES);
for (int i = 0; i < messageItems.length(); i++) {
JSONObject obj = messageItems.getJSONObject(i);
String title = obj.getString(TITLE);
String message = obj.getString(MESSAGE);
}
} else {
Log.e("JSON Response", "success == 0");
}
} catch (Exception e) {
e.printStackTrace();
}
My code looks like the this:
Code:
//Message task
MessageTask task = new MessageTask(DashboardActivity.class);
task.execute();
try {
json = task.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
messageItems = json.getJSONArray(MESSAGES);
for (int i = 0; i < messageItems.length(); i++) {
JSONObject obj = messageItems.getJSONObject(i);
String title = obj.getString(TITLE);
String message = obj.getString(MESSAGE);
}
} else {
Log.e("JSON Response", "success == 0");
}
} catch (Exception e) {
e.printStackTrace();
}
I am using Async Task to retrieve my JSON.
And my JSON parser looks like this:
Code:
public class JSONParser {
static InputStream is = null;
static JSONObject jObj = null;
static String json = "";
// constructor
public JSONParser() {
}
public JSONObject getJSONFromUrl(String url, List<NameValuePair> params) {
// Making HTTP request
try {
// defaultHttpClient
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new UrlEncodedFormEntity(params));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
is, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
json = sb.toString();
Log.e("JSON", json);
} catch (Exception e) {
Log.e("Buffer Error", "Error converting result " + e.toString());
}
// try parse the string to a JSON object
try {
jObj = new JSONObject(json);
} catch (JSONException e) {
Log.e("JSON Parser", "Error parsing data " + e.toString());
}
// return JSON String
return jObj;
}
}
But it doesn't get through the if statement, because it can't find the value success. ( org.json.JSONException: No value for success ). Don't really know what I am doing wrong here. Is it because I am using AsyncTask and retrieving my JSON the wrong way?
I also renamed my Array to "messages", stupid mistake thanks!
CodeMonkeyy said:
But it doesn't get through the if statement, because it can't find the value success. ( org.json.JSONException: No value for success ). Don't really know what I am doing wrong here. Is it because I am using AsyncTask and retrieving my JSON the wrong way?
I also renamed my Array to "messages", stupid mistake thanks!
Click to expand...
Click to collapse
We're getting closer! With regards to using AsyncTask - thats fine and the recommended way to do service side sync/download operations (doesn't block the UI thread) so no need to change that.
I just took a look at my reference JSON and I have the success tag outside of an item eg:
Code:
{"topical":[{"tid":"5","title":"Exam countdown... just 12 weeks left!","story":"some_story_text_here","staff":"0","red":"0","show":"1"}],[COLOR="red"]"success":1[/COLOR]}
Whereas your success tag is put in each item:
Code:
{"message":{"2":[{"uid":"2","title":"","message":"Test1","[COLOR="red"]success":1,[/COLOR]"created_at":null,"updated_at":null}],"3":[{"uid":"3","title":"","message":"Test2 !",[COLOR="red"]"success":1[/COLOR],"created_at":null,"updated_at":null}],"4":[{"uid":"4","title":"Bla","message":"Test3!","[COLOR="Red"]success":1[/COLOR],"created_at":null,"updated_at":null}]}}
I'm guessing that your php line for:
PHP:
$response["success"] = 1;
is inside of the while loop:
PHP:
while ($row = mysql_fetch_array($result)) {
Taking it out of the while loop should fix that
That makes sense, because I am trying to get a success code for my whole JSON response. I changed my PHP code to:
Code:
while($row = mysqli_fetch_array( $messages )) {
// create rowArr
$rowArr = array(
'uid' => $row['id'],
'title' => $row['title'],
'message' => $row["message"],
'created_at' => $row['created_at'],
'updated_at' => $row['updated_at'],
);
// store rowArr in $return_arr
$return_arr[$row['id']][] = $rowArr;
}
$return_arr['success'] = 1;
// Json encode
echo json_encode(array("messages" => $return_arr));
}
Retrieving the following JSON:
Code:
{"messages":{"2":[{"uid":"2","title":"","message":"Test1","created_at":null,"updated_at":null}],"3":[{"uid":"3","title":"","message":"Test2 !","created_at":null,"updated_at":null}],"4":[{"uid":"4","title":"Bla","message":"Test3!","created_at":null,"updated_at":null}],"success":1}}
But I am still getting the following exception:
Code:
org.json.JSONException: No value for success
The success tag is still being encoded as part of an inner array, not the first array - try this:
PHP:
if (mysql_num_rows($messages) > 0) {
$response["messages"] = array();
while ($row = mysql_fetch_array($messages)) {
$messagesArray= array(
'uid' => $row['id'],
'title' => $row['title'],
'message' => $row['message'],
'created_at' => $row['created_at'],
'updated_at' => $row['updated_at'],
);
array_push($response["messages"], $messagesArray);
}
$response["success"] = 1;
echo json_encode($response);
} else {
$response["success"] = 0;
$response["message"] = "No messages found";
echo json_encode($response);
}
And it's finally working!
Getting the following JSON result:
Code:
{"tag":"message","success":1,"error":0,"messages":[{"uid":"2","title":"","message":"Test1","created_at":null,"updated_at":null},{"uid":"3","title":"","message":"Test2!","created_at":null,"updated_at":null},{"uid":"4","title":"Bla","message":"Test3!","created_at":null,"updated_at":null}]}
And I can successfully loop through my JSON with the following code:
Code:
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
messageItems = json.getJSONArray(MESSAGES);
for (int i = 0; i < messageItems.length(); i++) {
JSONObject obj = messageItems.getJSONObject(i);
String title = obj.getString(TITLE);
String message = obj.getString(MESSAGE);
Log.e("TITLE :", title);
Log.e("MESSAGE :", message);
}
} else {
Log.e("JSON Response", "success == 0");
}
} catch (Exception e) {
e.printStackTrace();
}
Thank you very much! So the problem was that I placed the "success" tag outside my Array?